2.29. Struct a binární data¶
Modul struct zabaluje hodnoty Pythonu do pevného binárního rozložení a rozbaluje bajty zpět na hodnoty Pythonu. Sáhněte po něm, když pracujete s binárním formátem souboru, síťovým protokolem nebo zařízením, které vyměňuje záznamy pevné velikosti.
Většinu případů pokrývají dvě funkce:
struct.pack()– vezme hodnoty Pythonu a formátovací řetězec a vrátí objektbytess přesným rozložením.struct.unpack()– vezme formátovací řetězec a objektbytesa vrátí n-tici hodnot Pythonu.
2.29.1. Formátovací řetězce¶
Formátovací řetězec uvádí jeden kód na každé pole v záznamu. Kódy popisují jak velikost, tak interpretaci každého pole.
Typ int v Pythonu nemá pevnou velikost – roste tak, aby se vešla jakákoli hodnota, kterou mu přiřadíte. Binární formáty pevné velikosti mají: každé celočíselné pole používá dohodnutý počet bajtů. Modul struct převádí mezi neomezenými inty Pythonu a těmito reprezentacemi pevné velikosti.
Šířka celého čísla je počet bitů, které používá. Jeden bajt je osm bitů. Malé písmeno označuje variantu se znaménkem; velké písmeno variantu bez znaménka (pouze nezáporné hodnoty):
b/B– 8bitové (jeden bajt).-128..127se znaménkem,0..255bez znaménka.h/H– 16bitové (dva bajty).-32768..32767se znaménkem,0..65535bez znaménka.i/I– 32bitové (čtyři bajty). Přibližně ±dvě miliardy se znaménkem, čtyři miliardy bez znaménka.q/Q– 64bitové (osm bajtů). Pro běžné použití prakticky neomezené.
Zvolte šířku, která pohodlně pokryje rozsah, který očekáváte. Zabalení hodnoty mimo deklarovaný rozsah ji buď tiše přetočí, nebo vyvolá struct.error, podle konkrétního sestavení.
Zbývající běžné kódy jsou pro čísla s plovoucí desetinnou čárkou a bajtové řetězce:
f– 32bitové float (jednoduchá přesnost; přibližně sedm desetinných číslic). Běžný typfloatv MicroPythonu má již tuto velikost, takže jeho zabalení dofje bezeztrátové.d– 64bitové float (dvojitá přesnost; přibližně patnáct desetinných číslic). Zabalení 32bitového typufloatMicroPythonu dodjej rozšíří na osm bajtů, ale nepřidá žádnou přesnost.s– bajtový řetězec pevné délky, kterému předchází počet (8spro osmibajtové pole).
2.29.2. Pořadí bajtů¶
Vícebajtové celé číslo lze v paměti uložit dvěma způsoby. Číslo 0x12345678 ve 32bitovém poli je rozloženo takto:
Little-endian – nejméně významný bajt první:
78 56 34 12.Big-endian – nejvíce významný bajt první:
12 34 56 78.
Obojí kóduje stejnou hodnotu; liší se pouze v tom, na kterém konci pole je nízký bajt. Soubor zapsaný jedním systémem je při čtení jiným systémem zkomolený, pokud se pořadí bajtů neshoduje.
Pořadí volí úvodní znak formátovacího řetězce:
<– little-endian. Běžné na x86 a ARM.>– big-endian. Běžné v síťových protokolech.!– síťové pořadí, ekvivalentní>.
Bez úvodního znaku se použije nativní pořadí bajtů a nativní zarovnání; explicitní nastavení < nebo > tuto nejednoznačnost odstraní a je obvykle to, co chcete při čtení souboru nebo komunikaci s jiným počítačem.
Poznámka
OpenMV Cam je little-endian – stejně jako jeho hostitelský počítač. Ve formátovacích řetězcích používejte < pro soubory lokální pro kameru a pro binární data, která putují na desktop nebo z něj. Použijte > (nebo !) pro síťové protokoly a pro jakýkoli formát, jehož specifikace vyžaduje big-endian.
"<HI" zabalí 16bitovou hodnotu následovanou 32bitovou hodnotou do šesti bajtů v pořadí little-endian.¶
2.29.3. Zabalování¶
import struct
blob = struct.pack("<HI", 320, 1000000)
print(blob, len(blob))
Výstup:
b'@\x01@B\x0f\x00' 6
Formát <HI vytvoří šest bajtů: dva pro pole H a čtyři pro pole I, vše v pořadí little-endian. Předejte přesně tolik hodnot, kolik formát očekává – nesoulad vyvolá struct.error.
2.29.4. Rozbalování¶
width, count = struct.unpack("<HI", blob)
print(width, count)
Výstup:
320 1000000
struct.unpack() vždy vrací n-tici, i když formát popisuje jediné pole. Pro lepší čitelnost ji rozbalte na stejném řádku.
2.29.5. Bajtové řetězce pevné délky¶
Kód s čte nebo zapisuje blok bajtů doslovně. Počet jde před s – 4s znamená „čtyři bajty zpracované jako jeden bajtový řetězec“. To je obvyklý způsob, jak do záznamu vložit magickou hodnotu, značku pevné velikosti nebo doplněné pole se jménem:
header = struct.pack("<4sHI", b"OMV0", 320, 1000000)
print(header)
Výstup:
b'OMV0@\x01@B\x0f\x00'
Prvními čtyřmi bajty je doslovná magická hodnota b"OMV0"; další dva jsou pole H (320); poslední čtyři jsou pole I (1000000). Rozbalení vrátí bajty zpět jako objekt bytes:
magic, width, count = struct.unpack("<4sHI", header)
print(magic, width, count)
Výstup:
b'OMV0' 320 1000000
Pokud je zdrojová hodnota kratší než deklarovaný počet, výsledek je zprava doplněn znakem \x00; pokud je delší, přebytečné bajty jsou tiše zahozeny:
struct.pack("4s", b"hi") # b'hi\x00\x00'
struct.pack("4s", b"toolong") # b'tool'
Počet je délka v bajtech, nikoli počet znaků – s pracuje se surovými bajty, takže řetězec UTF-8 s vícebajtovými znaky je třeba nejprve .encode() a počítat v bajtech.
2.29.6. Velikost a částečné čtení¶
struct.calcsize() vrací počet bajtů, které formátovací řetězec spotřebuje:
struct.calcsize("<HI") # 6
Při čtení proudu záznamů ze souboru přečtěte na každý záznam přesně tolik bajtů:
record_size = struct.calcsize("<HI")
with open("data.bin", "rb") as f:
while True:
chunk = f.read(record_size)
if len(chunk) < record_size:
break
width, count = struct.unpack("<HI", chunk)
print(width, count)
Krátké čtení na konci souboru vytvoří blok menší než record_size – považujte to za podmínku konce proudu, místo abyste se pokoušeli rozbalit částečný záznam.