Lesen einer Binärdatei mit Python

103

Ich finde es besonders schwierig, Binärdateien mit Python zu lesen. Kannst du mir helfen? Ich muss diese Datei lesen, die in Fortran 90 leicht zu lesen ist

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)

Im Detail ist das Dateiformat:

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. 

Wie kann ich das mit Python lesen? Ich habe alles versucht, aber es hat nie funktioniert. Gibt es eine Möglichkeit, dass ich ein f90-Programm in Python verwende, diese Binärdatei lese und dann die Daten speichere, die ich verwenden muss?

Brian
quelle
1
Wurde diese Datei von einem Fortran-Programm geschrieben? Wenn ja, wie wurde es geschrieben, da Fortran standardmäßig vor jedem Datensatz, den es in die Datei schreibt, zusätzliche Daten hinzufügt. Möglicherweise müssen Sie beim Lesen der Daten darauf achten.
Chris
1
Bitte ignorieren Sie meinen vorherigen Kommentar, die Interger 8 und 4 * N sind eindeutig diese zusätzlichen Daten.
Chris
2
Siehe auch Antworten auf die Frage zum Lesen von Binärdateien in Python .
Chris
Die fromfileFunktion von Numpy erleichtert das Lesen von Binärdateien. Ich empfehle es.
littleO
... und achten Sie immer auf Ihre Endian-Nessen, insb. beim Portieren zwischen Computern verschiedener Hersteller.
DragonLord

Antworten:

152

Lesen Sie den Inhalt der Binärdatei folgendermaßen:

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

dann "entpacken" Sie Binärdaten mit struct.unpack :

Die Startbytes: struct.unpack("iiiii", fileContent[:20])

Der Körper: Ignoriere die Überschriftenbytes und das nachfolgende Byte (= 24); Der verbleibende Teil bildet den Körper. Um die Anzahl der Bytes im Körper zu kennen, wird eine ganzzahlige Division durch 4 durchgeführt. Der erhaltene Quotient wird mit der Zeichenfolge multipliziert 'i', um das richtige Format für die Entpackmethode zu erstellen:

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

Das Endbyte: struct.unpack("i", fileContent[-4:])

Gecco
quelle
Können Sie sich bitte diesen anderen Beitrag ansehen? stackoverflow.com/questions/8092469/… ... Ich soll wieder eine andere Binärdatei lesen, aber in diesem Fall kenne ich die Bytestruktur nicht im Detail. Zum Beispiel habe ich herausgefunden, dass es manchmal die Ganzzahl 8 gibt. Mit IDL ist es jedoch wirklich einfach, diese Daten zu lesen. Kann ich dasselbe mit Python machen?
Brian
Bitte geben Sie (im anderen Beitrag, nicht hier) an, warum Sie mit den veröffentlichten Antworten und Kommentaren nicht zufrieden sind. Vielleicht sollten Sie die Frage auch aktualisieren, um weitere Details bereitzustellen ... Ich werde sie mir ansehen, wenn sie aktualisiert wird.
Gecco
Sehen Sie diese Antwort , wenn Sie eine entpackte char [] in einen String konvertieren müssen.
PeterM
import struct
JW
23

Im Allgemeinen würde ich empfehlen, dass Sie sich mit der Verwendung des Strukturmoduls von Python befassen . Es ist Standard bei Python und es sollte einfach sein, die Spezifikation Ihrer Frage in eine Formatierungszeichenfolge zu übersetzen, die für geeignet ist struct.unpack().

Beachten Sie, dass Sie, wenn zwischen den Feldern "unsichtbare" Auffüllungen vorhanden sind, dies herausfinden und in den unpack()Aufruf aufnehmen müssen, da Sie sonst die falschen Bits lesen.

Das Lesen des Inhalts der Datei, um etwas zum Entpacken zu haben, ist ziemlich trivial:

import struct

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

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

Dadurch werden die ersten beiden Felder entpackt, vorausgesetzt, sie beginnen ganz am Anfang der Datei (kein Auffüllen oder fremde Daten), und es wird auch die native Bytereihenfolge (das @Symbol) angenommen. Die Is in der Formatierungszeichenfolge bedeuten "Ganzzahl ohne Vorzeichen, 32 Bit".

entspannen
quelle
ok, aber ich weiß nicht einmal, wie man die Bytes der Datei liest. Wie kann ich aus meiner Frage die Datei von den Bytes 5 bis 8 lesen und dann das Ergebnis in eine Ganzzahl konvertieren? Entschuldigung, aber ich bin neu in Python.
Brian
14

Sie können verwenden numpy.fromfile, die Daten sowohl aus Text- als auch aus Binärdateien lesen können. Sie würden zuerst einen Datentyp erstellen, der Ihr Dateiformat darstellt numpy.dtype, und diesen Typ dann mit aus der Datei lesen numpy.fromfile.

Chris
quelle
2
Leicht zu übersehen! Dokumente sind etwas dünn; siehe reddit.com/r/Python/comments/19q8nt/… für eine Diskussion
verloren
10

So lesen Sie eine Binärdatei in ein bytesObjekt:

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

So erstellen Sie ein intByte 0-3 der Daten:

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

So entpacken Sie mehrere ints aus den Daten:

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

Ich fand auch, dass Python beim Lesen und Schreiben von Binärdateien fehlt, deshalb habe ich ein kleines Modul geschrieben (für Python 3.6+).

Mit binaryfile würden Sie so etwas tun (ich vermute , da ich Fortran nicht kenne):

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)

Was eine Ausgabe wie diese erzeugt:

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

Ich habe skip () verwendet, um die zusätzlichen Daten zu überspringen, die Fortran hinzufügt, aber Sie können stattdessen ein Dienstprogramm hinzufügen, um Fortran-Datensätze ordnungsgemäß zu behandeln. In diesem Fall wäre eine Pull-Anfrage willkommen.

Fax
quelle
-2
import pickle
f=open("filename.dat","rb")
try:
    while True:
        x=pickle.load(f)
        print x
except EOFError:
    pass
f.close()
Eeshitri
quelle
5
Wahrscheinlich nur eine kleine Erklärung wert, warum dies besser ist als (oder zumindest so gut wie) andere Antworten.
Phil
2
Haben Sie getestet, ob dies mit der von fortran generierten Binärdatei funktioniert?
Agentp
1
Und erklären Sie auch, was es tut ... Was ist Gurke? Was wird pickle.loadgeladen? Lädt es einen Fortran-Stream, direkte oder sequentielle Dateien? Sie sind unterschiedlich und nicht kompatibel.
Vladimir F