Leyendo un archivo binario en una estructura en Python

Tengo un archivo binario con un formato / estructura conocido.

¿Cómo leo todos los datos binarios en una matriz de la estructura?

Algo así como (en pseudo código)

bytes = read_file(filename) struct = {'int','int','float','byte[255]'} data = read_as_struct(bytes, struct) data[1] >>> 10,11,10.1,Arr[255] 

EDITAR:

Solución hasta ahora:

 data = [] fmt = '=iiiii256i' fmt_s = '=iiiii' fmt_spec = '256i' struct_size = struct.calcsize(fmt) for i in range(struct_size, len(bytes)-struct_size, struct_size): dat1= list(struct.unpack(fmt_s, bytes[i-struct_size:i-1024])) dat2= list(struct.unpack(fmt_spec, bytes[i-1024:i])) dat1.append(dat2) data.append(dat1) 

Usa el módulo de struct ; necesita definir los tipos en un formato de cadena documentado con esa biblioteca:

 struct.unpack('=HHf255s', bytes) 

El ejemplo anterior espera un orden de bytes nativo, dos cortos sin firmar, un flotante y una cadena de 255 caracteres.

Para hacer un bucle sobre una cadena de bytes ya completamente leída, usaría itertools ; Hay una receta de mero útil que he adaptado aquí:

 from itertools import izip_longest, imap from struct import unpack, calcsize fmt_s = '=5i' fmt_spec = '=256i' size_s = calcsize(fmt_s) size = size_s + calcsize(fmt_spec) def chunked(iterable, n, fillvalue=''): args = [iter(iterable)] * n return imap(''.join, izip_longest(*args, fillvalue=fillvalue)) data = [unpack(fmt_s, section[:size_s]) + (unpack(fmt_spec, section[size_s:]),) for section in chunked(bytes, size)] 

Esto produce tuplas en lugar de listas, pero es lo suficientemente fácil de ajustar si tiene que:

 data = [list(unpack(fmt_s, section[:size_s])) + [list(unpack(fmt_spec, section[size_s:]))] for section in chunked(bytes, size)] 

En realidad, parece que estás intentando leer una lista (o matriz) de estructuras del archivo. La forma idiomática de hacer esto en Python es usar el módulo struct y llamar a struct.unpack() en un bucle, ya sea un número fijo de veces si conoce el número de ellos por adelantado, o hasta que se scope el final del archivo. y almacenar los resultados en una list . Aquí hay un ejemplo de este último:

 import struct struct_fmt = '=5if255s' # int[5], float, byte[255] struct_len = struct.calcsize(struct_fmt) struct_unpack = struct.Struct(struct_fmt).unpack_from results = [] with open(filename, "rb") as f: while True: data = f.read(struct_len) if not data: break s = struct_unpack(data) results.append(s) 

Los mismos resultados también se pueden obtener de forma un poco más concisa utilizando una lista de comprensión junto con un breve ayudante de la función del generador (es decir, read_chunks() continuación):

 def read_chunks(f, length): while True: data = f.read(length) if not data: break yield data with open(filename, "rb") as f: results = [struct_unpack(chunk) for chunk in read_chunks(f, struct_len)] 

Añadir comentarios

 import struct 

Primero solo lee el binario en una matriz

 mbr = file('mbrcontent', 'rb').read() 

Así que solo puedes buscar un pedazo de la matriz

 partition_table = mbr[446:510] 

y luego descomprimirlo como un número entero

 signature = struct.unpack(' 

un ejemplo más complejo

 little_endian = (signature == 0xaa55) # should be True print "Little endian:", little_endian PART_FMT = (little_endian and '<' or '>') + ( "B" # status (0x80 = bootable (active), 0x00 = non-bootable) # CHS of first block "B" # Head "B" # Sector is in bits 5; bits 9 of cylinder are in bits 7-6 "B" # bits 7-0 of cylinder "B" # partition type # CHS of last block "B" # Head "B" # Sector is in bits 5; bits 9 of cylinder are in bits 7-6 "B" # bits 7-0 of cylinder "L" # LBA of first sector in the partition "L" # number of blocks in partition, in little-endian format ) PART_SIZE = 16 fmt_size = struct.calcsize(PART_FMT) # sanity check expectations assert fmt_size == PART_SIZE, "Partition format string is %i bytes, not %i" % (fmt_size, PART_SIZE) def cyl_sector(sector_cyl, cylinder7_0): sector = sector_cyl & 0x1F # bits 5-0 # bits 7-6 of sector_cyl contain bits 9-8 of the cylinder cyl_high = (sector_cyl >> 5) & 0x03 cyl = (cyl_high << 8) | cylinder7_0 return sector, cyl #I have corrected the indentation, but the change is refused because less than 6 characters, so I am adding this useful comment. for partition in range(4): print "Partition #%i" % partition, offset = PART_SIZE * partition (status, start_head, start_sector_cyl, start_cyl7_0, part_type, end_head, end_sector_cyl, end_cyl7_0, lba, blocks ) = struct.unpack( PART_FMT,partition_table[offset:offset + PART_SIZE]) if status == 0x80: print "Bootable", elif status: print "Unknown status [%s]" % hex(status), print "Type=0x%x" % part_type start = (start_head,) + cyl_sector(start_sector_cyl, start_cyl7_0) end = (end_head,) + cyl_sector(end_sector_cyl, end_cyl7_0) print " (Start: Heads:%i\tCyl:%i\tSect:%i)" % start print " (End: Heads:%i\tCyl:%i\tSect:%i)" % end print " LBA:", lba print " Blocks:", blocks