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.