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.