Odczytywanie liczb całkowitych z pliku binarnego w Pythonie

83

Próbuję odczytać plik BMP w Pythonie. Wiem, że pierwsze dwa bajty wskazują firmę BMP. Następne 4 bajty to rozmiar pliku. Kiedy wykonuję:

fin = open("hi.bmp", "rb")
firm = fin.read(2)  
file_size = int(fin.read(4))  

Dostaję:

ValueError: nieprawidłowy literał int () o podstawie 10: „F # \ x13”

Chcę odczytać te cztery bajty jako liczbę całkowitą, ale wygląda na to, że Python odczytuje je jako znaki i zwraca ciąg, którego nie można przekonwertować na liczbę całkowitą. Jak mogę to zrobić poprawnie?

Manuel Araoz
źródło
2
Jeśli Twoim celem jest użycie bitmapy zamiast spędzać czas na pisaniu własnej biblioteki BMP (nie to nie brzmi jak zabawa ...), możesz użyć PIL pythonware.com/products/pil, który być może już masz zainstalowany. Spróbuj: zaimportuj obraz
Jared Updike
7
Dzięki Jared, ale chciałem ręcznie przeczytać bmp tylko po to, żeby się dobrze bawić! :)
Manuel Araoz

Odpowiedzi:

123

readMetoda zwraca sekwencję bajtów jako ciąg znaków. Aby przekonwertować ciąg bajtów na dane binarne, użyj wbudowanego structmodułu: http://docs.python.org/library/struct.html .

import struct

print(struct.unpack('i', fin.read(4)))

Zauważ, że unpackzawsze zwraca krotkę, więc struct.unpack('i', fin.read(4))[0]podaje wartość całkowitą, której szukasz.

Prawdopodobnie powinieneś użyć łańcucha formatującego '<i'(<jest modyfikatorem, który wskazuje kolejność bajtów little-endian oraz standardowy rozmiar i wyrównanie - domyślnie jest używana kolejność bajtów, rozmiar i wyrównanie platformy). Zgodnie ze specyfikacją formatu BMP, bajty powinny być zapisane w kolejności bajtów Intel / little-endian.

codeape
źródło
22
Zamiast pisać i = struct.unpack(...)[0], często piszęi, = struct.unpack(...)
Otto Allmendinger
@Otto Czy jest jakiś powód, dla którego wolisz jeden sposób od drugiego? Czy jest jakaś logiczna różnica?
Caltor
2
Uważam za bardzo zaskakujące, że nie ma wbudowanej funkcji do odczytu liczb całkowitych (lub szortów itp.) Z pliku w Pythonie. Nie jestem ekspertem od Java, ale uważam, że ma do tego natywne funkcje, takie jak readUnsignedShort ().
Caltor
@codeape Czy możesz zdefiniować, co robi [0] lub przynajmniej jaki to typ elementu językowego. Nie jest to od razu widoczne i prawie niemożliwe jest wyszukiwanie w dokumentacji Pythona.
Caltor
W przypadku list i krotek obj [N] oznacza: pobierz N-ty element obj. Zobacz docs.python.org/tutorial/introduction.html#lists
codeape
50

Alternatywną metodą, która nie korzysta z funkcji „struct.unpack ()”, byłoby użycie NumPy :

import numpy as np

f = open("file.bin", "r")
a = np.fromfile(f, dtype=np.uint32)

„dtype” reprezentuje typ danych i może być typu int #, uint #, float #, complex # lub typu zdefiniowanego przez użytkownika. Zobacz numpy.fromfile.

Osobiście wolę używać NumPy do pracy z danymi tablicowymi / macierzowymi, ponieważ jest to o wiele szybsze niż korzystanie z list Pythona.

Emanuel Ey
źródło
13
Otwarcie pliku można pominąć:a = np.fromfile('file.bin', dtype=np.uint32)
Mathieu Schopfer
17

Od wersji Python 3.2+ możesz to również osiągnąć przy użyciu from_bytesnatywnej metody int:

file_size = int.from_bytes(fin.read(2), byteorder='big')

Zwróć uwagę, że ta funkcja wymaga określenia, czy liczba jest zakodowana w formacie big- czy little-endian, więc będziesz musiał określić endian-ness, aby upewnić się, że działa poprawnie.

CrepeGoat
źródło
6

Oprócz tego structmożesz również użyć arraymodule

import array
values = array.array('l') # array of long integers
values.read(fin, 1) # read 1 integer
file_size  = values[0]
Nick Dandoulakis
źródło
Słuszna uwaga. Ale to rozwiązanie nie jest tak elastyczne, jak w module struct, ponieważ wszystkie elementy odczytywane przez wartości. Read () muszą być długimi liczbami całkowitymi (nie jest wygodnie czytać długą liczbę całkowitą, bajt, a następnie długą liczbę całkowitą, moduł tablicy).
Eric O Lebigot
Zgadzam się. arrayjest skutecznym sposobem odczytu pliku binarnego, ale niezbyt elastycznym, gdy mamy do czynienia ze strukturą, jak słusznie wspomniałeś.
Nick Dandoulakis
1
array.read jest przestarzała na rzecz array.fromfile od 1.51
4

Gdy czytasz plik binarny, musisz rozpakować go na liczbę całkowitą, więc użyj do tego modułu struct

import struct
fin = open("hi.bmp", "rb")
firm = fin.read(2)  
file_size, = struct.unpack("i",fin.read(4))
Anurag Uniyal
źródło
struct.unpack zwraca krotkę
luc
1

Podczas odczytu z pliku binarnego używany jest typ danych zwany bajtami. Przypomina to trochę listę lub krotkę, z wyjątkiem tego, że może przechowywać tylko liczby całkowite od 0 do 255.

Próbować:

file_size = fin.read(4)
file_size0 = file_size[0]
file_size1 = file_size[1]
file_size2 = file_size[2]
file_size3 = file_size[3]

Lub:

file_size = list(fin.read(4))

Zamiast:

file_size = int(fin.read(4))
Super S
źródło