2.29. Struct és bináris adatok¶
A struct modul a Python értékeket egy rögzített bináris elrendezésbe csomagolja, és a bájtokat visszafejtve ismét Python értékekké alakítja. Akkor érdemes elővenni, amikor bináris fájlformátummal, hálózati protokollal vagy rögzített méretű rekordokat cserélő eszközzel dolgozol.
Két függvény lefedi az esetek többségét:
struct.pack()– Python értékeket és egy formátumsztringet vesz át, és pontosan a megadott elrendezésűbytesobjektumot ad vissza.struct.unpack()– egy formátumsztringet és egybytesobjektumot vesz át, és Python értékek egy tuple-jét adja vissza.
2.29.1. Formátumsztringek¶
A formátumsztring a rekord minden mezőjéhez egy kódot sorol fel. A kódok írják le az egyes mezők méretét és értelmezését is.
A Python int típusának nincs rögzített mérete – akkorára nő, amekkora értéket hozzárendelsz. A bináris formátumoknak van rögzített méretük: minden egész mező egy előre megállapodott számú bájtot használ. A struct a korlátlan Python egészek és ezek rögzített méretű ábrázolásai között végez átalakítást.
Egy egész szám szélessége a felhasznált bitek száma. Egy bájt nyolc bit. A kisbetűs kód az előjeles változat; a nagybetűs kód az előjel nélküli (csak nem negatív értékek):
b/B– 8 bites (egy bájt).-128..127előjellel,0..255előjel nélkül.h/H– 16 bites (két bájt).-32768..32767előjellel,0..65535előjel nélkül.i/I– 32 bites (négy bájt). Hozzávetőleg ±kétmilliárd előjellel, négymilliárd előjel nélkül.q/Q– 64 bites (nyolc bájt). A mindennapi használatban gyakorlatilag korlátlan.
Válassz olyan szélességet, amely kényelmesen lefedi a várt tartományt. A deklarált tartományon kívüli érték csomagolása a build-től függően vagy némán körbefordul, vagy struct.error kivételt vált ki.
A többi gyakori kód a lebegőpontos számokhoz és a bájtsztringekhez tartozik:
f– 32 bites lebegőpontos szám (egyszeres pontosság; körülbelül hét tizedesjegy). A Python szokásosfloattípusa a MicroPython-on már eleve ekkora, így egy ilyen értékfformátumba csomagolása veszteségmentes.d– 64 bites lebegőpontos szám (kétszeres pontosság; körülbelül tizenöt tizedesjegy). Egy 32 bites MicroPythonfloatdformátumba csomagolása nyolc bájtra szélesíti, de nem ad hozzá pontosságot.s– rögzített hosszúságú bájtsztring, amelyet egy darabszám előz meg (8segy nyolc bájtos mező esetén).
2.29.2. Bájtsorrend¶
Egy többbájtos egész szám kétféleképpen tárolható a memóriában. A 0x12345678 szám egy 32 bites mezőben így van elhelyezve:
Little-endian – a legkisebb helyiértékű bájt elöl:
78 56 34 12.Big-endian – a legnagyobb helyiértékű bájt elöl:
12 34 56 78.
Mindkettő ugyanazt az értéket kódolja; csupán abban különböznek, hogy a mező melyik vége az alacsony bájt. Az egyik rendszer által írt fájl olvashatatlanná válik a másikon, ha a bájtsorrend nem egyezik.
A formátumsztring kezdő karaktere választja meg a sorrendet:
<– little-endian. Gyakori az x86 és az ARM platformokon.>– big-endian. Gyakori a hálózati protokollokban.!– hálózati sorrend, egyenértékű a>jellel.
Kezdő karakter nélkül a natív bájtsorrendet és a natív igazítást használja a rendszer; a < vagy > explicit megadása megszünteti ezt a kétértelműséget, és általában ez az, amire fájl olvasásakor vagy másik géppel való kommunikációkor szükséged van.
Megjegyzés
Az OpenMV Cam little-endian – ugyanúgy, mint a hozzá csatlakoztatott PC. A kamerán helyileg tárolt fájlokhoz és az asztali géppel oda-vissza utazó bináris adatokhoz használj < jelet a formátumsztringekben. A > (vagy !) jelet a hálózati protokollokhoz, valamint minden olyan formátumhoz használd, amelynek specifikációja big-endian sorrendet ír elő.
A "<HI" egy 16 bites értéket, majd egy 32 bites értéket csomagol hat little-endian bájtba.¶
2.29.3. Csomagolás¶
import struct
blob = struct.pack("<HI", 320, 1000000)
print(blob, len(blob))
Kimenet:
b'@\x01@B\x0f\x00' 6
A <HI formátum hat bájtot állít elő: kettőt a H mezőnek és négyet az I mezőnek, mind little-endian. Pontosan annyi értéket adj át, amennyit a formátum elvár – az eltérés struct.error kivételt vált ki.
2.29.4. Kicsomagolás¶
width, count = struct.unpack("<HI", blob)
print(width, count)
Kimenet:
320 1000000
A struct.unpack() mindig tuple-t ad vissza, még akkor is, ha a formátum egyetlen mezőt ír le. Az olvashatóság érdekében ugyanazon a soron csomagold ki.
2.29.5. Rögzített hosszúságú bájtsztringek¶
Az s kód szó szerint olvas vagy ír egy bájtdarabot. A darabszám az s elé kerül – a 4s jelentése „négy bájt egyetlen bájtsztringként kezelve”. Ez a szokásos módja annak, hogy egy magic értéket, egy rögzített méretű címkét vagy egy kitöltött névmezőt ágyazz be egy rekordba:
header = struct.pack("<4sHI", b"OMV0", 320, 1000000)
print(header)
Kimenet:
b'OMV0@\x01@B\x0f\x00'
Az első négy bájt a literál magic b"OMV0"; a következő kettő a H mező (320); az utolsó négy az I mező (1000000). A kicsomagolás a bájtokat bytes objektumként adja vissza:
magic, width, count = struct.unpack("<4sHI", header)
print(magic, width, count)
Kimenet:
b'OMV0' 320 1000000
Ha a forrásérték rövidebb a deklarált darabszámnál, az eredmény jobbról \x00 bájtokkal lesz kitöltve; ha hosszabb, a felesleges bájtokat némán eldobja:
struct.pack("4s", b"hi") # b'hi\x00\x00'
struct.pack("4s", b"toolong") # b'tool'
A darabszám egy bájthossz, nem karakterszám – az s nyers bájtokkal dolgozik, így egy többbájtos karaktereket tartalmazó UTF-8 sztringet először .encode()-olni kell, és bájtokban kell megszámolni.
2.29.6. Méretezés és részleges olvasások¶
A struct.calcsize() visszaadja, hány bájtot fogyaszt egy formátumsztring:
struct.calcsize("<HI") # 6
Amikor rekordok folyamát olvasod egy fájlból, rekordonként pontosan ennyi bájtot olvass be:
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)
A fájl végén egy rövid olvasás a record_size méretnél kisebb darabot eredményez – ezt a stream végét jelző feltételként kezeld, ahelyett, hogy megpróbálnál egy részleges rekordot kicsomagolni.