Leggere un file binario con python


104

Trovo particolarmente difficile leggere file binari con Python. Puoi darmi una mano? Ho bisogno di leggere questo file, che in Fortran 90 è facilmente leggibile

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)

In dettaglio, il formato del file è:

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. 

Come posso leggerlo con Python? Ho provato di tutto ma non ha mai funzionato. C'è qualche possibilità che io possa usare un programma f90 in Python, leggendo questo file binario e quindi salvare i dati che devo usare?


1
Questo file è stato scritto da un programma Fortran? Se è così, come è stato scritto, poiché Fortran, per impostazione predefinita, aggiunge dati aggiuntivi prima di ogni record che scrive su file. Potrebbe essere necessario prestare attenzione durante la lettura dei dati.
Chris

1
Si prega di ignorare il mio commento precedente, gli intergers 8 e 4 * N sono chiaramente questi dati aggiuntivi.
Chris

2
Inoltre, vedi le risposte alla domanda sulla lettura del file binario in python .
Chris

La fromfilefunzione di Numpy rende facile leggere i file binari. Lo consiglio.
littleO

... e fai sempre attenzione alle tue endian-ness, esp. durante il trasferimento tra computer di produttori diversi.
DragonLord

Risposte:


155

Leggi il contenuto del file binario in questo modo:

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

quindi "decomprimere" i dati binari utilizzando struct.unpack :

I byte iniziali: struct.unpack("iiiii", fileContent[:20])

Il corpo: ignora i byte dell'intestazione e il byte finale (= 24); La parte restante forma il corpo, per conoscere il numero di byte nel corpo fare una divisione intera per 4; Il quoziente ottenuto viene moltiplicato per la stringa 'i'per creare il formato corretto per il metodo unpack:

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

Il byte finale: struct.unpack("i", fileContent[-4:])


Puoi dare un'occhiata a quest'altro post? stackoverflow.com/questions/8092469/… ... Sto ancora per leggere un altro file binario, ma in questo caso non conosco la struttura dei byte nei dettagli. Ad esempio, ho capito che a volte c'è il numero intero 8. Tuttavia, con IDL è davvero semplice leggere questi dati. Posso fare lo stesso con Python?
Brian il

Per favore indica (all'interno dell'altro post, non qui) perché non sei soddisfatto delle risposte e dei commenti pubblicati. Forse dovresti anche aggiornare la domanda per fornire maggiori dettagli ... Lo guarderò quando sarà aggiornato.
gecco

Vedi questa risposta se hai bisogno di convertire un carattere decompresso [] in una stringa.
PeterM

import struct
JW

23

In generale, consiglierei di esaminare l'utilizzo del modulo struct di Python per questo. È standard con Python e dovrebbe essere facile tradurre la specifica della tua domanda in una stringa di formattazione adatta a struct.unpack().

Nota che se c'è un riempimento "invisibile" tra / intorno ai campi, dovrai capirlo e includerlo nella unpack()chiamata, altrimenti leggerai i bit sbagliati.

Leggere il contenuto del file per avere qualcosa da scompattare è piuttosto banale:

import struct

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

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

Questo decomprime i primi due campi, supponendo che inizino all'inizio del file (nessun riempimento o dati estranei) e assumendo anche l'ordine dei byte nativo (il @simbolo). La Is nella stringa di formattazione significa "intero senza segno, 32 bit".


ok, ma non so nemmeno come leggere i byte del file. Dalla mia domanda come posso leggere il file dai byte 5 a 8 e quindi convertire il risultato in un numero intero? Scusa, ma sono nuovo con Python.
Brian


11

Per leggere un file binario in un bytesoggetto:

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

Per creare un intdai byte 0-3 dei dati:

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

Per decomprimere più messaggi intdi posta elettronica dai dati:

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

0

Anch'io ho trovato Python carente quando si tratta di leggere e scrivere file binari, quindi ho scritto un piccolo modulo (per Python 3.6+).

Con binaryfile faresti qualcosa del genere (immagino, dato che non conosco 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)

Che produce un output come questo:

{
    '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
}

Ho usato skip () per saltare i dati aggiuntivi aggiunti da Fortran, ma potresti voler aggiungere un'utilità per gestire correttamente i record Fortran. Se lo fai, una richiesta pull sarebbe la benvenuta.


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

6
Probabilmente vale solo una piccola spiegazione del motivo per cui questo è migliore (o almeno buono come) altre risposte.
Phil

2
hai testato e verificato che funzioni con il binario generato da fortran?
agentp

1
E spiega anche cosa fa ... Cos'è il sottaceto? Cosa pickle.loadcarica? Carica un flusso Fortran, file diretti o sequenziali? Sono diversi e non compatibili.
Vladimir F
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.