Lesen von Ganzzahlen aus einer Binärdatei in Python

79

Ich versuche eine BMP- Datei in Python zu lesen . Ich weiß, dass die ersten beiden Bytes die BMP-Firma angeben. Die nächsten 4 Bytes sind die Dateigröße. Wenn ich ausführe:

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

Ich bekomme:

ValueError: ungültiges Literal für int () mit Basis 10: 'F # \ x13'

Ich möchte diese vier Bytes als Ganzzahl lesen, aber Python liest sie anscheinend als Zeichen und gibt eine Zeichenfolge zurück, die nicht in eine Ganzzahl konvertiert werden kann. Wie kann ich das richtig machen?

Manuel Araoz
quelle
2
Wenn Sie die Bitmap verwenden möchten, anstatt Zeit mit dem Schreiben Ihrer eigenen BMP-Bibliothek zu verbringen (nicht, dass das nicht nach Spaß klingt ...), können Sie PIL pythonware.com/products/pil verwenden, das Sie möglicherweise bereits installiert haben. Versuchen Sie: Bild importieren
Jared Updike
6
Danke Jared, aber ich wollte den BMP manuell lesen, nur um Spaß zu haben! :)
Manuel Araoz

Antworten:

120

Die readMethode gibt eine Folge von Bytes als Zeichenfolge zurück. Verwenden Sie das integrierte structModul http://docs.python.org/library/struct.html, um von einer Zeichenfolgenbyte-Sequenz in Binärdaten zu konvertieren .

import struct

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

Beachten Sie, dass unpackimmer ein Tupel zurückgegeben wird, also struct.unpack('i', fin.read(4))[0]der ganzzahlige Wert, nach dem Sie suchen.

Sie sollten wahrscheinlich die Formatzeichenfolge verwenden '<i'(<ist ein Modifikator, der die Little-Endian-Bytereihenfolge sowie die Standardgröße und -ausrichtung angibt - standardmäßig wird die Bytereihenfolge, -größe und -ausrichtung der Plattform verwendet). Gemäß der BMP-Formatspezifikation sollten die Bytes in Intel / Little-Endian-Bytereihenfolge geschrieben werden.

Codeape
quelle
21
Anstatt zu schreiben, schreibe i = struct.unpack(...)[0]ich ofti, = struct.unpack(...)
Otto Allmendinger
@Otto Gibt es einen Grund, warum Sie einen Weg dem anderen vorziehen? Gibt es einen logischen Unterschied?
Caltor
2
Ich finde es sehr überraschend, dass es keine integrierte Funktion zum Lesen von Ganzzahlen (oder Shorts usw.) aus einer Datei in Python gibt. Ich bin kein Java-Experte, aber ich glaube, es hat native Funktionen wie readUnsignedShort (), um dies zu tun.
Caltor
@codeape Könnten Sie bitte definieren, was die [0] tut oder zumindest welche Art von Sprachelement es ist? Es ist nicht sofort ersichtlich und es ist fast unmöglich, in der Python-Dokumentation danach zu suchen.
Caltor
Für Listen und Tupel bedeutet obj [N]: Holen Sie sich das N-te Element von obj. Siehe docs.python.org/tutorial/introduction.html#lists
codeape
48

Eine alternative Methode, die 'struct.unpack ()' nicht verwendet, wäre die Verwendung von NumPy :

import numpy as np

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

'dtype' stellt den Datentyp dar und kann int #, uint #, float #, complex # oder ein benutzerdefinierter Typ sein. Siehe numpy.fromfile.

Persönlich bevorzugen Sie die Verwendung von NumPy für die Arbeit mit Array- / Matrixdaten, da diese viel schneller sind als die Verwendung von Python-Listen.

Emanuel Ey
quelle
13
Das Öffnen der Datei kann übersprungen werden:a = np.fromfile('file.bin', dtype=np.uint32)
Mathieu Schopfer
16

Ab Python 3.2+ können Sie dies auch mit der from_bytesnativen int-Methode erreichen:

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

Beachten Sie, dass Sie für diese Funktion angeben müssen, ob die Nummer im Big- oder Little-Endian-Format codiert ist. Daher müssen Sie die Endianität bestimmen, um sicherzustellen, dass sie ordnungsgemäß funktioniert.

CrepeGoat
quelle
6

Außer structSie können auch arrayModul verwenden

import array
values = array.array('l') # array of long integers
values.read(fin, 1) # read 1 integer
file_size  = values[0]
Nick Dandoulakis
quelle
Guter Punkt. Diese Lösung ist jedoch nicht so flexibel wie die des Strukturmoduls, da alle Elemente, die values.read () durchlesen, lange Ganzzahlen sein müssen (es ist nicht bequem, eine lange Ganzzahl, ein Byte und dann eine lange Ganzzahl mit dem zu lesen Array-Modul).
Eric O Lebigot
Genau. arrayist eine effiziente Methode zum Lesen einer Binärdatei, aber nicht sehr flexibel, wenn wir uns mit Struktur befassen müssen, wie Sie richtig erwähnt haben.
Nick Dandoulakis
1
array.read ist seit
4

Während Sie die Binärdatei lesen, müssen Sie sie in eine Ganzzahl entpacken. Verwenden Sie dazu das Strukturmodul

import struct
fin = open("hi.bmp", "rb")
firm = fin.read(2)  
file_size, = struct.unpack("i",fin.read(4))
Anurag Uniyal
quelle
struct.unpack gibt ein Tupel zurück
luc
1

Wenn Sie aus einer Binärdatei lesen, wird ein Datentyp namens Bytes verwendet. Dies ist ein bisschen wie Liste oder Tupel, außer dass nur ganze Zahlen von 0 bis 255 gespeichert werden können.

Versuchen:

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]

Oder:

file_size = list(fin.read(4))

Anstatt:

file_size = int(fin.read(4))
Super S.
quelle