Hier sind einige Tipps zum Arbeiten mit Binärdateien in Python.
Es gibt zwei Möglichkeiten, mit Binärdateien in Python zu arbeiten: das Modul struct
und die Klasse ctypes.Structure
.
Grundsätzlich verwendet das Modul struct
die Klasse ctypes.Structure
, wenn Sie einige Bytes Binär verarbeiten möchten oder wenn Sie mit mehr Bytes oder C / C ++ arbeiten möchten.
struct
ModulLesen wir als Beispiel die Binärdatei einer PNG-Datei. In einer PNG-Datei sind die ersten 8 Bytes im Header festgelegt. Die 9. bis 18. Datenbytes werden im IHDR-Bereich (um genau zu sein Teil des IHDR), in der vertikalen und horizontalen Größe des Bildes, in der Bittiefe und im Farbmodus gespeichert.
import struct
png_data = open("sample.png ", "rb").read()
struct.unpack_from(">I4sIIBB", png_data, 8)
# (13, b'IHDR', 250, 156, 8, 2)
Sie können die Daten mit struct.unpack
lesen, aber Sie erhalten eine Fehlermeldung, wenn der Offset und die Größe des von Ihnen angegebenen Puffers nicht genau gleich sind.
Struct.unpack_from
ist nützlich, wenn Sie einen Teil der Daten lesen möchten.
Beim Lesen der Binärdatei kommt das Putten (Staubbereich zum Ausrichten) auf jeden Fall heraus. Das "x" -Format ist praktisch, da es die Daten überspringt.
data = b'd\x00\xb0\x04'
# NG
kind, _, value = struct.unpack("BBH", data)
# Yes!
kind, value = struct.unpack("BxH", data)
Die Klasse struct.Struct
ist eine Klassifizierung der Formatzeichenfolge des Moduls struct
.
Da das Format analysiert wird, wenn die Klasse instanziiert wird, ist es schneller, die Instanz im Voraus zu erstellen, wenn in der Schleife wiederholt "packen" / "entpacken".
Es ist verwirrend mit der Klasse ctypes.Structre
.
point = struct.Struct("HH")
for x, y in zip(range(10), range(10)):
point.pack(x, y)
Brief | C Sprachtyp | Standardgröße |
---|---|---|
x | Biss setzen | 1 |
c | char | 1 |
b | signed char | 1 |
B | unsigned char, BYTE | 1 |
? | _Bool | 1 |
h | short | 2 |
H | unsinged short, WORD | 2 |
i | int | 4 |
I | unsigned int, DWORD | 4 |
l | long, LONG | 4 |
L | unsigned long, ULONG | 4 |
q | long long, LONGLONG | 8 |
Q | unsigned long long, ULONGLONG | 8 |
n | ssize_t(Python3.3 oder später) | Nur einheimisch |
N | size_t(Python3.3 oder später) | Nur einheimisch |
f | float | 4 |
d | double | 8 |
s | char[] | - |
p | char[] | - |
P | void * | - |
Beispiel für Formatzeichen:
BITMAPINFOHEADER-Struktur
typedef struct tagBITMAPINFOHEADER {
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
Zeichen im BITMAPINFOHEADER-Strukturformat
"IllHHIIllII"
Brief | Bytereihenfolge | Größe | Ausrichtung |
---|---|---|---|
@ | Native | Native | Native |
= | Native | Standardgröße | Keiner |
< | Kleiner Inder | Standardgröße | Keiner |
> | Big Endian | Standardgröße | Keiner |
! | Big Endian | Standardgröße | Keiner |
@Wann=Der Unterschied von(CPU=amd64,OS=Ubuntu64bit)
struct.calcsize("BI")
# 8
struct.calcsize("=BI")
# 5
Beachten Sie, dass die Ausrichtung "keine" lautet, wenn Sie den Endian explizit angeben.
Sie können mit C / C ++ - Strukturen in der Klasse ctypes.Structure
arbeiten.
Wenn Sie versuchen, viele Daten mit dem Modul 'Struktur' zu lesen, ähnelt das Format einem Zauberspruch. Wenn Sie also viele Binärdaten fest lesen möchten, sollten Sie die Klasse ctypes.Structure
verwenden. Machen wir das.
Erben Sie ctypes.Structure
und definieren Sie die Typen in _field_
.
from ctypes import *
"""
typedef struct {
char identity[4];
uint16_t x;
uint16_t y;
} TestStructure;
"""
class TestStructure(Structure):
_fields_ = (
('identity', c_char * 4),
('x', c_uint16),
('y', c_uint16),
)
Die Instanz ist wie folgt definiert.
t = TestStructure(b"TEST", 100, 100)
In der Sprache C ändert sich die Größe von "int" und "short" je nach Umgebung. Da C99 die Angabe von Typen mit fester Größe wie "int16_t" und "int32_t" ermöglicht, geben Sie die feste Größe so weit wie möglich an. Sollte benutzt werden. Verwenden Sie auf der Python-Seite außerdem Typen mit fester Größe wie "ctypes.c_int16" anstelle von "ctypes.c_int".
Sie können schreiben, indem Sie die Instanz "ctypes.Structure" so übergeben, wie es ist, um "io" oder "FILE" zu "schreiben".
import io
buffer = io.BytesIO()
buffer.write(TestStructure(b"TEST", 100, 100))
buffer.getvalue()
# b'TESTd\x00d\x00'
Sie können es lesen, indem Sie die Instanz "ctypes.Structure" an "readinto" übergeben.
buffer = io.BytesIO(b'TESTd\x00d\x00')
t = TestStructure()
buffer.readinto(t)
t.identity, t.x, t.y
# (b'TEST', 100, 100)
Die Versatzposition des Strukturelements kann mit der Klassenmethode class name.member name.offset
ermittelt werden.
class Point(Structure):
_fields_ = (
('x', c_uint16),
('y', c_uint16),
)
Point.y.offset
# 2
sizeof
Sie können die Größe der Struktur mit ctypes.sizeof
ermitteln.
class TestStructure(Structure):
_fields_ = (
('flags', c_ubyte),
('value', c_int32),
)
sizeof(TestStructure)
# 8
memset / memmove Die Entsprechungen der C-Sprache "memset" und "memmove" sind "ctypes.memset" und "ctypes.memmove".
c_array = (c_char * 12)()
memset(c_array, 0, sizeof(c_array))
memmove(c_array, b"test\x00", len(b"test\x00"))
Sie können die Daten zuordnen, indem Sie die Zeiger der Struktur wie in C / C ++ umwandeln. Wenn Sie den Zeiger der Struktur angeben möchten, wandeln Sie ihn mit "ctypes.POINTER", "ctypes.cast" um. Sie können den Wert, auf den der Zeiger verweist, mit "content" abrufen.
class PointText(Structure):
_fields_ = (
('x', c_uint16),
('y', c_uint16),
('text', c_char * 0),
)
data = b'd\x00d\x00null terminate text\x00'
p_point = cast(data, POINTER(Point))
p_point.contents.x, p_point.contents.y
# (200, 120)
#NULL-terminierte Zeichenfolge lesen
string_at(addressof(p_point.contents) + PointText.text.offset)
# b'null terminate text'
Lesen Sie die NULL-terminierte Zeichenfolge mit ctypes.stering_at
und verwenden Sie ctypes.wstring_at
für Unicode.
Beachten Sie jedoch, dass die Zeigermanipulation Python selbst zum Absturz bringen kann. Vermeiden Sie nach Möglichkeit nicht angegebene Mitglieder wie "char []".
Sie können ein "ctypes" -Objekt mit "memoryview" in ein "PyObject" konvertieren.
p = Point(200, 120)
memoryview(p).tobytes()
# b'\xc8\x00x\x00'
class BPoint(BigEndianStructure):
_fields_ = (
('x', c_uint16),
('y', c_uint16),
)
class LPoint(LittleEndianStructure):
_fields_ = (
('x', c_uint16),
('y', c_uint16),
)
bpoint = BPoint(0x0102, 0x0304)
lpoint = LPoint(0x0102, 0x0304)
memoryview(bpoint).tobytes()
# b'\x01\x02\x03\x04'
memoryview(lpoint).tobytes()
# b'\x02\x01\x04\x03'
http://docs.python.jp/3.5/library/struct.html http://docs.python.jp/3.5/library/ctypes.html
Recommended Posts