Czytanie pliku binarnego za pomocą Pythona


105

Szczególnie trudno jest mi odczytać plik binarny w Pythonie. Czy może Pan mi pomóc? Muszę przeczytać ten plik, który w Fortranie 90 łatwo czyta

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)

Dokładniej mówiąc, format pliku to:

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. 

Jak mogę to przeczytać w Pythonie? Próbowałem wszystkiego, ale nigdy nie działało. Czy jest szansa, że ​​mógłbym użyć programu F90 w Pythonie, odczytać ten plik binarny, a następnie zapisać dane, których potrzebuję?


1
Czy ten plik został napisany przez program w języku Fortran? Jeśli tak, w jaki sposób został napisany, ponieważ Fortran domyślnie dodaje dodatkowe dane przed każdym rekordem, który zapisuje do pliku. Podczas odczytywania danych może być konieczne zachowanie ostrożności.
Chris,

1
Proszę zignorować mój poprzedni komentarz, intergery 8 i 4 * N są wyraźnie tymi dodatkowymi danymi.
Chris,

2
Zobacz także odpowiedzi na pytanie dotyczące czytania pliku binarnego w Pythonie .
Chris,

fromfileFunkcja Numpy ułatwia odczytywanie plików binarnych. Polecam to.
littleO

... i zawsze uważaj na swoje endianiny, zwł. podczas przenoszenia między komputerami różnych producentów.
DragonLord

Odpowiedzi:


157

Przeczytaj zawartość pliku binarnego w ten sposób:

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

następnie „rozpakuj” dane binarne za pomocą struct.unpack :

Bajty początkowe: struct.unpack("iiiii", fileContent[:20])

Treść: zignoruj ​​bajty nagłówka i bajt końcowy (= 24); Pozostała część tworzy treść, aby poznać liczbę bajtów w treści, wykonaj podział całkowity przez 4; Otrzymany iloraz jest mnożony przez ciąg znaków, 'i'aby utworzyć poprawny format dla metody rozpakowywania:

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

Bajt końcowy: struct.unpack("i", fileContent[-4:])


Czy możesz spojrzeć na ten drugi post? stackoverflow.com/questions/8092469/ ... ... znowu mam czytać inny plik binarny, ale w tym przypadku nie znam szczegółów struktury bajtów. Na przykład doszedłem do wniosku, że czasami występuje liczba całkowita 8. Jednak w przypadku IDL odczytanie tych danych jest naprawdę proste. Czy mogę zrobić to samo z Pythonem?
Brian

Proszę wskazać (w innym poście, nie tutaj), dlaczego nie jesteś zadowolony z opublikowanych odpowiedzi i komentarzy. Być może powinieneś również zaktualizować pytanie, aby podać więcej szczegółów ... Przyjrzę się temu, gdy zostanie zaktualizowane.
gecco

Zobacz odpowiedź, jeśli chcesz przekonwertować rozpakowany znak [] na ciąg.
PeterM

import struct
JW

23

Ogólnie polecam przyjrzenie się do tego celu wykorzystania modułu struct w Pythonie . Jest to standard w Pythonie i powinno być łatwe do przetłumaczenia specyfikacji twojego pytania na ciąg formatujący odpowiedni dlastruct.unpack() .

Zwróć uwagę, że jeśli istnieje „niewidoczne” wypełnienie między polami / wokół nich, będziesz musiał to rozgryźć i uwzględnić w unpack()wywołaniu, w przeciwnym razie odczytasz niewłaściwe bity.

Odczytanie zawartości pliku w celu wypakowania czegoś jest dość trywialne:

import struct

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

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

Spowoduje to rozpakowanie dwóch pierwszych pól, zakładając, że zaczynają się na samym początku pliku (bez wypełniania lub zbędnych danych), a także przy założeniu natywnej kolejności bajtów ( @symbol). W Is w ciągu formatowania oznacza „liczbę całkowitą bez znaku, 32 bitów”.


ok, ale nawet nie wiem, jak odczytać bajty pliku. Z mojego pytania, jak mogę odczytać plik z bajtów od 5 do 8, a następnie przekonwertować wynik na liczbę całkowitą? Przepraszam, ale jestem nowy w Pythonie.
Brian,


11

Aby odczytać plik binarny z bytesobiektu:

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

Aby utworzyć intz bajtów 0-3 danych:

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

Aby rozpakować wiele plików intz danych:

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

0

Mnie też brakuje Pythona, jeśli chodzi o czytanie i pisanie plików binarnych, więc napisałem mały moduł (dla Pythona 3.6+).

Z plikiem binarnym zrobiłbyś coś takiego (zgaduję, skoro nie znam Fortrana):

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)

Który daje taki wynik:

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

Użyłem skip (), aby pominąć dodatkowe dane, które dodaje Fortran, ale zamiast tego możesz dodać narzędzie do poprawnej obsługi rekordów Fortran. Jeśli to zrobisz, żądanie ściągnięcia byłoby mile widziane.


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

7
Prawdopodobnie warto tylko trochę wyjaśnić, dlaczego jest to lepsze (lub przynajmniej tak dobre jak) inne odpowiedzi.
Phil

2
czy przetestowałeś i zweryfikowałeś, że działa z plikiem binarnym wygenerowanym przez Fortran?
agentp

1
A także wyjaśnij, co to robi ... Co to jest marynata? Co się pickle.loadładuje? Czy ładuje strumień Fortran, pliki bezpośrednie czy sekwencyjne? Są różne i nie są kompatybilne.
Vladimir F
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.