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ę?

Brian
źródło
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:])

gecco
źródło
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”.

rozwijać
źródło
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,
14

Możesz użyć numpy.fromfile, który może czytać dane zarówno z plików tekstowych, jak i binarnych. Najpierw należy skonstruować typ danych, który reprezentuje format pliku, używając numpy.dtype, a następnie odczytać ten typ z pliku za pomocą numpy.fromfile.

Chris
źródło
2
Łatwo to przeoczyć! Dokumenty są trochę cienkie; zobacz reddit.com/r/Python/comments/19q8nt/ ... do jakiejś dyskusji
zagubiony
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])
Eugene Yarmash
źródło
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.

Faks
źródło
-2
import pickle
f=open("filename.dat","rb")
try:
    while True:
        x=pickle.load(f)
        print x
except EOFError:
    pass
f.close()
Eeshitri
źródło
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