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í objekt bytes s přesným rozložením.

  • struct.unpack() – vezme formátovací řetězec a objekt bytes a 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 / B8bitové (jeden bajt). -128..127 se znaménkem, 0..255 bez znaménka.

  • h / H16bitové (dva bajty). -32768..32767 se znaménkem, 0..65535 bez znaménka.

  • i / I32bitové (čtyři bajty). Přibližně ±dvě miliardy se znaménkem, čtyři miliardy bez znaménka.

  • q / Q64bitové (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ý typ float v MicroPythonu má již tuto velikost, takže jeho zabalení do f je bezeztrátové.

  • d – 64bitové float (dvojitá přesnost; přibližně patnáct desetinných číslic). Zabalení 32bitového typu float MicroPythonu do d jej rozšíří na osm bajtů, ale nepřidá žádnou přesnost.

  • s – bajtový řetězec pevné délky, kterému předchází počet (8s pro 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.

Šest bajtů uspořádaných v řadě, kde první dva bajty jsou seskupeny jako pole "H" (16bitové bez znaménka) a další čtyři jako pole "I" (32bitové bez znaménka), každé označené svým pořadím bajtů little-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 s4s 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.