2.29. Struct i binarni podaci¶
Modul struct pakira Python vrijednosti u fiksni binarni raspored i raspakira bajtove natrag u Python vrijednosti. Posegnite za njim kada radite s binarnim formatom datoteke, mrežnim protokolom ili uređajem koji razmjenjuje zapise fiksne veličine.
Dvije funkcije pokrivaju većinu slučajeva:
struct.pack()– uzima Python vrijednosti i format string, vraćabytesobjekt točno zadanog rasporeda.struct.unpack()– uzima format string ibytesobjekt, vraća n-torku Python vrijednosti.
2.29.1. Format stringovi¶
Format string navodi po jedan kod za svako polje u zapisu. Kodovi opisuju i veličinu i interpretaciju svakog polja.
Python int nema fiksnu veličinu – raste kako bi stao u bilo koju vrijednost koju mu dodijelite. Binarni formati imaju fiksne veličine: svako cjelobrojno polje koristi dogovoreni broj bajtova. struct pretvara između neograničenih Python cijelih brojeva i tih prikaza fiksne veličine.
Širina cijelog broja je broj bitova koje koristi. Jedan bajt je osam bitova. Mali tiskani kod je predznačena varijanta; veliki tiskani kod je nepredznačena (samo nenegativne vrijednosti):
b/B– 8-bitni (jedan bajt).-128..127predznačeno,0..255nepredznačeno.h/H– 16-bitni (dva bajta).-32768..32767predznačeno,0..65535nepredznačeno.i/I– 32-bitni (četiri bajta). Otprilike ±dvije milijarde predznačeno, četiri milijarde nepredznačeno.q/Q– 64-bitni (osam bajtova). Za svakodnevnu uporabu praktički neograničeno.
Odaberite širinu koja udobno pokriva raspon koji očekujete. Pakiranje vrijednosti izvan deklariranog raspona ili tiho prelije (wrap around) ili podigne struct.error, ovisno o buildu.
Preostali uobičajeni kodovi su za brojeve s pomičnim zarezom i nizove bajtova:
f– 32-bitni broj s pomičnim zarezom (jednostruka preciznost; oko sedam decimalnih znamenki). Uobičajeni Pythonfloatna MicroPythonu već je ove veličine, pa je pakiranje ufbez gubitaka.d– 64-bitni broj s pomičnim zarezom (dvostruka preciznost; oko petnaest decimalnih znamenki). Pakiranje 32-bitnog MicroPythonfloatudproširuje ga na osam bajtova, ali ne dodaje preciznost.s– niz bajtova fiksne duljine, kojem prethodi broj (8sza polje od osam bajtova).
2.29.2. Redoslijed bajtova¶
Višebajtni cijeli broj može se pohraniti u memoriju na dva načina. Broj 0x12345678 u 32-bitnom polju raspoređen je ovako:
Little-endian – najmanje značajan bajt prvi:
78 56 34 12.Big-endian – najznačajniji bajt prvi:
12 34 56 78.
Oba kodiraju istu vrijednost; razlikuju se samo u tome koji kraj polja sadrži niski bajt. Datoteku koju je napisao jedan sustav drugi sustav iščita iskrivljeno ako se redoslijed bajtova ne podudara.
Vodeći znak format stringa odabire redoslijed:
<– little-endian. Uobičajen na x86 i ARM.>– big-endian. Uobičajen u mrežnim protokolima.!– mrežni redoslijed, ekvivalentan>.
Bez vodećeg znaka koriste se nativni redoslijed bajtova i nativno poravnanje; eksplicitno postavljanje < ili > uklanja tu nejednoznačnost i obično je ono što želite pri čitanju datoteke ili komunikaciji s drugim računalom.
Napomena
OpenMV Cam je little-endian – isto kao i njegovo host računalo. Koristite < u format stringovima za datoteke lokalne kameri i za binarne podatke koji putuju prema ili od stolnog računala. Koristite > (ili !) za mrežne protokole i za svaki format čija specifikacija zahtijeva big-endian.
"<HI" pakira 16-bitnu vrijednost iza koje slijedi 32-bitna vrijednost u šest little-endian bajtova.¶
2.29.3. Pakiranje¶
import struct
blob = struct.pack("<HI", 320, 1000000)
print(blob, len(blob))
Ispis:
b'@\x01@B\x0f\x00' 6
Format <HI proizvodi šest bajtova: dva za polje H i četiri za polje I, sve little-endian. Proslijedite točno onaj broj vrijednosti koji format očekuje – nepodudaranje podiže struct.error.
2.29.4. Raspakiranje¶
width, count = struct.unpack("<HI", blob)
print(width, count)
Ispis:
320 1000000
struct.unpack() uvijek vraća n-torku, čak i kada format opisuje jedno polje. Raspakirajte je u istom retku radi čitljivosti.
2.29.5. Nizovi bajtova fiksne duljine¶
Kod s čita ili piše komad bajtova doslovno. Broj ide ispred s – 4s znači „četiri bajta tretirana kao jedan niz bajtova”. Ovo je uobičajen način ugradnje magične vrijednosti, oznake (tag) fiksne veličine ili dopunjenog polja imena u zapis:
header = struct.pack("<4sHI", b"OMV0", 320, 1000000)
print(header)
Ispis:
b'OMV0@\x01@B\x0f\x00'
Prva četiri bajta su doslovna magična vrijednost b"OMV0"; sljedeća dva su polje H (320); posljednja četiri su polje I (1000000). Raspakiranje vraća bajtove natrag kao bytes objekt:
magic, width, count = struct.unpack("<4sHI", header)
print(magic, width, count)
Ispis:
b'OMV0' 320 1000000
Ako je izvorna vrijednost kraća od deklariranog broja, rezultat se s desne strane dopunjuje s \x00; ako je dulja, višak bajtova se tiho odbacuje:
struct.pack("4s", b"hi") # b'hi\x00\x00'
struct.pack("4s", b"toolong") # b'tool'
Broj je duljina u bajtovima, a ne broj znakova – s radi sa sirovim bajtovima, pa UTF-8 niz s višebajtnim znakovima prvo treba .encode()-ati i prebrojati u bajtovima.
2.29.6. Određivanje veličine i djelomična čitanja¶
struct.calcsize() vraća broj bajtova koje format string troši:
struct.calcsize("<HI") # 6
Pri čitanju toka zapisa iz datoteke, pročitajte točno toliko bajtova po zapisu:
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)
Kratko čitanje na kraju datoteke proizvodi komad manji od record_size – tretirajte to kao uvjet kraja toka umjesto da pokušavate raspakirati djelomičan zapis.