Leyendo un archivo binario con Python


104

Encuentro particularmente difícil leer archivos binarios con Python. ¿Puedes darme una mano? Necesito leer este archivo, que en Fortran 90 es fácilmente leído por

int*4 n_particles, n_groups
real*4 group_id(n_particles)
read (*) n_particles, n_groups
read (*) (group_id(j),j=1,n_particles)

En detalle, el formato de archivo es:

Bytes 1-4 -- The integer 8.
Bytes 5-8 -- The number of particles, N.
Bytes 9-12 -- The number of groups.
Bytes 13-16 -- The integer 8.
Bytes 17-20 -- The integer 4*N.
Next many bytes -- The group ID numbers for all the particles.
Last 4 bytes -- The integer 4*N. 

¿Cómo puedo leer esto con Python? Intenté todo pero nunca funcionó. ¿Existe alguna posibilidad de que pueda usar un programa f90 en Python, leer este archivo binario y luego guardar los datos que necesito usar?


1
¿Este archivo fue escrito por un programa de Fortran? Si es así, ¿cómo se escribió? Ya que Fortran, por defecto, agrega datos adicionales antes de cada registro que escribe en el archivo. Es posible que deba tener cuidado con esto al leer los datos.
Chris

1
Por favor ignore mi comentario anterior, los números enteros 8 y 4 * N son claramente estos datos adicionales.
Chris

2
Además, vea las respuestas a la pregunta sobre la lectura de un archivo binario en python .
Chris

La fromfilefunción de Numpy facilita la lectura de archivos binarios. Lo recomiendo.
littleO

... y siempre ten cuidado con tus endian-nesses, esp. al migrar entre computadoras de diferentes fabricantes.
DragonLord

Respuestas:


155

Lea el contenido del archivo binario como este:

with open(fileName, mode='rb') as file: # b is important -> binary
    fileContent = file.read()

luego "descomprime" los datos binarios usando struct.unpack :

Los bytes de inicio: struct.unpack("iiiii", fileContent[:20])

El cuerpo: ignore los bytes de encabezado y el byte final (= 24); La parte restante forma el cuerpo, para saber el número de bytes en el cuerpo haz una división entera entre 4; El cociente obtenido se multiplica por la cadena 'i'para crear el formato correcto para el método de descomprimir:

struct.unpack("i" * ((len(fileContent) -24) // 4), fileContent[20:-4])

El byte final: struct.unpack("i", fileContent[-4:])


¿Puedes echar un vistazo a esta otra publicación? stackoverflow.com/questions/8092469/… ... De nuevo voy a leer otro archivo binario, pero en este caso no conozco la estructura de bytes en detalles. Por ejemplo, descubrí que a veces existe el entero 8. Sin embargo, con IDL es realmente sencillo leer estos datos. ¿Puedo hacer lo mismo con Python?
Brian

Indique (dentro de la otra publicación, no aquí) por qué no está satisfecho con las respuestas y comentarios publicados. Quizás también debería actualizar la pregunta para proporcionar más detalles ... La echaré un vistazo cuando se actualice.
gecco

Vea esta respuesta si necesita convertir un char [] descomprimido en una cadena.
PeterM

import struct
JW

23

En general, le recomendaría que considere usar el módulo struct de Python para esto. Es estándar en Python, y debería ser fácil traducir la especificación de su pregunta a una cadena de formato adecuada para struct.unpack().

Tenga en cuenta que si hay un relleno "invisible" entre / alrededor de los campos, tendrá que averiguarlo e incluirlo en la unpack()llamada, o leerá los bits incorrectos.

Leer el contenido del archivo para tener algo que descomprimir es bastante trivial:

import struct

data = open("from_fortran.bin", "rb").read()

(eight, N) = struct.unpack("@II", data)

Esto descomprime los dos primeros campos, asumiendo que comienzan desde el principio del archivo (sin relleno o datos extraños), y también asumiendo el orden de bytes nativo (el @símbolo). La Is en la cadena de formato significa "entero sin signo, 32 bits".


ok, pero ni siquiera sé cómo leer los bytes del archivo. De mi pregunta, ¿cómo puedo leer el archivo de los bytes 5 a 8 y luego convertir el resultado a un número entero? Lo siento, pero soy nuevo en Python.
Brian


11

Para leer un archivo binario en un bytesobjeto:

from pathlib import Path
data = Path('/path/to/file').read_bytes()  # Python 3.5+

Para crear un intdesde los bytes 0-3 de los datos:

i = int.from_bytes(data[:4], byteorder='little', signed=False)

Para descomprimir varios correos electrónicos intde los datos:

import struct
ints = struct.unpack('iiii', data[:16])

0

Yo también encontré que Python carece de lectura y escritura de archivos binarios, así que escribí un pequeño módulo (para Python 3.6+).

Con binaryfile harías algo como esto (supongo, ya que no conozco a Fortran):

import binaryfile

def particle_file(f):
    f.array('group_ids')  # Declare group_ids to be an array (so we can use it in a loop)
    f.skip(4)  # Bytes 1-4
    num_particles = f.count('num_particles', 'group_ids', 4)  # Bytes 5-8
    f.int('num_groups', 4)  # Bytes 9-12
    f.skip(8)  # Bytes 13-20
    for i in range(num_particles):
        f.struct('group_ids', '>f')  # 4 bytes x num_particles
    f.skip(4)

with open('myfile.bin', 'rb') as fh:
    result = binaryfile.read(fh, particle_file)
print(result)

Lo que produce una salida como esta:

{
    'group_ids': [(1.0,), (0.0,), (2.0,), (0.0,), (1.0,)],
    '__skipped': [b'\x00\x00\x00\x08', b'\x00\x00\x00\x08\x00\x00\x00\x14', b'\x00\x00\x00\x14'],
    'num_particles': 5,
    'num_groups': 3
}

Usé skip () para omitir los datos adicionales que agrega Fortran, pero es posible que desee agregar una utilidad para manejar los registros de Fortran correctamente. Si lo hace, sería bienvenida una solicitud de extracción.


-2
import pickle
f=open("filename.dat","rb")
try:
    while True:
        x=pickle.load(f)
        print x
except EOFError:
    pass
f.close()

6
Probablemente valga la pena una pequeña explicación de por qué esto es mejor (o al menos tan bueno) que otras respuestas.
Phil

2
¿Ha probado y verificado que esto funciona con el binario generado por fortran?
agentp

1
Y también explica qué hace ... ¿Qué es el pepinillo? ¿Qué pickle.loadcarga? ¿Carga un flujo de Fortran, archivos directos o secuenciales? Son diferentes y no compatibles.
Vladimir F
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.