Een binair bestand lezen met Python

Ik vind bijzonder moeilijk binair bestand met Python. Kunt u mij een handje helpen?
Ik moet dit bestand lezen, dat in Fortran 90 gemakkelijk wordt gelezen door

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 detail is het bestandsformaat:

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. 

Hoe kan ik dit lezen met Python? Ik heb alles geprobeerd, maar het heeft nooit gewerkt. Is er een kans dat ik een F90-programma in Python zou kunnen gebruiken, dit binaire bestand lezen en vervolgens de gegevens opslaan die ik moet gebruiken?


Antwoord 1, Autoriteit 100%

Lees de binaire bestandsinhoud zoals deze:

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

Vervolgens “Pak” binaire gegevens uit met struct.unpack :

De start bytes: struct.unpack("iiiii", fileContent[:20])

Het lichaam: negeer de kop bytes en de achterste byte (= 24); Het resterende deel vormt het lichaam, om het aantal bytes in het lichaam te kennen, doet een integer-divisie met 4; Het verkregen quotiënt wordt vermenigvuldigd met de string 'i'om het juiste formaat voor de Unpack-methode te maken:

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

De eindbyte: struct.unpack("i", fileContent[-4:])


Antwoord 2, autoriteit 12%

Over het algemeen raad ik je aan om hiervoor de module structvan Python te gebruiken . Het is standaard bij Python en het zou gemakkelijk moeten zijn om de specificatie van uw vraag te vertalen in een opmaakreeks die geschikt is voor struct.unpack().

Houd er rekening mee dat als er “onzichtbare” opvulling tussen/rond de velden is, u dat moet uitzoeken en opnemen in de unpack()-aanroep, anders leest u de verkeerde stukjes.

Het lezen van de inhoud van het bestand om iets uit te pakken is vrij triviaal:

import struct
data = open("from_fortran.bin", "rb").read()
(eight, N) = struct.unpack("@II", data)

Hiermee worden de eerste twee velden uitgepakt, ervan uitgaande dat ze helemaal aan het begin van het bestand beginnen (geen opvulling of externe gegevens), en ook uitgaande van de native byte-volgorde (het symbool @). De Ien in de opmaakreeks betekenen “unsigned integer, 32 bits”.


Antwoord 3, autoriteit 9%

Een binair bestand lezen naar een bytesobject:

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

Een intmaken van bytes 0-3 van de gegevens:

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

Om meerdere ints uit de gegevens uit te pakken:

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

Antwoord 4, autoriteit 8%

Je zou numpy.fromfile, die gegevens kan lezen uit zowel tekst- als binaire bestanden. U zou eerst een gegevenstype maken, dat uw bestandsindeling vertegenwoordigt, met behulp van numpy.dtypeen lees dit type vervolgens uit het bestand met numpy.fromfile.


Antwoord 5

Ook ik vond Python tekortschieten als het gaat om het lezen en schrijven van binaire bestanden, dus schreef ik een kleine module (voor Python 3.6+).

Met binaryfilezou je zoiets als dit doen (ik vermoed, aangezien ik niet ik ken Fortran niet):

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)

die een uitvoer als volgt produceert:

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

Ik gebruikte Skip () om de extra gegevens Fortran-voegt toe te voegen, maar misschien wilt u een hulpprogramma toevoegen om in plaats daarvan fortran-records aan te pakken. Als u dat doet, is een pull-aanvraag welkom.


Antwoord 6

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

Other episodes