2.29. Struct ja binääridata¶
struct-moduuli pakkaa Python-arvot kiinteään binäärimuotoon ja purkaa tavut takaisin Python-arvoiksi. Käytä sitä, kun työskentelet binääritiedostomuodon, verkkoprotokollan tai kiinteäkokoisia tietueita vaihtavan laitteen kanssa.
Kaksi funktiota kattaa useimmat tapaukset:
struct.pack()– ottaa Python-arvot ja muotoilumerkkijonon ja palauttaa täsmälleen oikean muotoisenbytes-objektin.struct.unpack()– ottaa muotoilumerkkijonon jabytes-objektin ja palauttaa Python-arvojen monikon.
2.29.1. Muotoilumerkkijonot¶
Muotoilumerkkijono luettelee yhden koodin kutakin tietueen kenttää kohti. Koodit kuvaavat sekä kunkin kentän koon että tulkinnan.
Pythonin int ei ole kiinteäkokoinen – se kasvaa sopimaan mihin tahansa arvoon, jonka sille annat. Binäärimuodot ovat kiinteäkokoisia: jokainen kokonaislukukenttä käyttää sovittua määrää tavuja. struct muuntaa rajoittamattomien Python-kokonaislukujen ja näiden kiinteäkokoisten esitysmuotojen välillä.
Kokonaisluvun leveys on bittien määrä, jonka se käyttää. Yksi tavu on kahdeksan bittiä. Pienaakkoskoodi on etumerkillinen muunnelma; suuraakkoskoodi on etumerkitön (vain ei-negatiiviset arvot):
b/B– 8-bittinen (yksi tavu).-128..127etumerkillisenä,0..255etumerkittömänä.h/H– 16-bittinen (kaksi tavua).-32768..32767etumerkillisenä,0..65535etumerkittömänä.i/I– 32-bittinen (neljä tavua). Noin ±kaksi miljardia etumerkillisenä, neljä miljardia etumerkittömänä.q/Q– 64-bittinen (kahdeksan tavua). Käytännössä rajoittamaton arkikäytössä.
Valitse leveys, joka kattaa mukavasti odottamasi arvoalueen. Ilmoitetun arvoalueen ulkopuolisen arvon pakkaaminen joko kiertää hiljaisesti ympäri tai nostaa struct.error-poikkeuksen, riippuen käännösversiosta.
Loput yleiset koodit ovat liukulukuja ja tavumerkkijonoja varten:
f– 32-bittinen liukuluku (yksinkertainen tarkkuus; noin seitsemän desimaalinumeroa). Pythonin tavallinenfloaton MicroPythonissa jo tämänkokoinen, joten sellaisen pakkaaminenf-muotoon on häviötöntä.d– 64-bittinen liukuluku (kaksinkertainen tarkkuus; noin viisitoista desimaalinumeroa). 32-bittisen MicroPython-float-arvon pakkaaminend-muotoon levittää sen kahdeksaan tavuun mutta ei lisää tarkkuutta.s– kiinteämittainen tavumerkkijono, jonka edellä on lukumäärä (8skahdeksantavuiselle kentälle).
2.29.2. Tavujärjestys¶
Monitavuinen kokonaisluku voidaan tallentaa muistiin kahdella tavalla. Luku 0x12345678 32-bittisessä kentässä asettuu näin:
Little-endian – vähiten merkitsevä tavu ensin:
78 56 34 12.Big-endian – eniten merkitsevä tavu ensin:
12 34 56 78.
Molemmat koodaavat saman arvon; ne ovat eri mieltä vain siitä, kumpi kentän pää on matala tavu. Yhden järjestelmän kirjoittama tiedosto on sekaisin, kun toinen lukee sen, jos tavujärjestys ei täsmää.
Muotoilumerkkijonon ensimmäinen merkki valitsee järjestyksen:
<– little-endian. Yleinen x86- ja ARM-arkkitehtuureissa.>– big-endian. Yleinen verkkoprotokollissa.!– verkkojärjestys, vastaa merkkiä>.
Ilman ensimmäistä merkkiä käytetään natiivia tavujärjestystä ja natiivia kohdistusta; <- tai >-merkin nimenomainen asettaminen poistaa tuon monitulkintaisuuden ja on yleensä juuri se, mitä haluat lukiessasi tiedostoa tai keskustellessasi toisen koneen kanssa.
Muista
OpenMV Cam on little-endian – sama kuin sen isäntä-PC. Käytä <-merkkiä muotoilumerkkijonoissa kameran paikallisille tiedostoille ja binääridatalle, joka kulkee pöytäkoneelle tai sieltä pois. Käytä >-merkkiä (tai !) verkkoprotokollissa ja kaikissa muodoissa, joiden määritys edellyttää big-endiania.
"<HI" pakkaa 16-bittisen arvon ja sen perään 32-bittisen arvon kuudeksi little-endian-tavuksi.¶
2.29.3. Pakkaaminen¶
import struct
blob = struct.pack("<HI", 320, 1000000)
print(blob, len(blob))
Tuloste:
b'@\x01@B\x0f\x00' 6
<HI-muoto tuottaa kuusi tavua: kaksi H-kentälle ja neljä I-kentälle, kaikki little-endian-järjestyksessä. Anna täsmälleen se määrä arvoja, jota muoto odottaa – ristiriita nostaa struct.error-poikkeuksen.
2.29.4. Purkaminen¶
width, count = struct.unpack("<HI", blob)
print(width, count)
Tuloste:
320 1000000
struct.unpack() palauttaa aina monikon, vaikka muoto kuvaisi yksittäisen kentän. Pura se samalla rivillä luettavuuden vuoksi.
2.29.5. Kiinteämittaiset tavumerkkijonot¶
s-koodi lukee tai kirjoittaa tavujoukon sellaisenaan. Lukumäärä tulee s-merkin eteen – 4s tarkoittaa ”neljä tavua käsiteltynä yhtenä tavumerkkijonona”. Tämä on tavanomainen tapa upottaa taikaluku, kiinteäkokoinen tunniste tai täytetty nimikenttä tietueeseen:
header = struct.pack("<4sHI", b"OMV0", 320, 1000000)
print(header)
Tuloste:
b'OMV0@\x01@B\x0f\x00'
Neljä ensimmäistä tavua ovat kirjaimellinen taikaluku b"OMV0"; seuraavat kaksi ovat H-kenttä (320); viimeiset neljä ovat I-kenttä (1000000). Purkaminen palauttaa tavut takaisin bytes-objektina:
magic, width, count = struct.unpack("<4sHI", header)
print(magic, width, count)
Tuloste:
b'OMV0' 320 1000000
Jos lähdearvo on lyhyempi kuin ilmoitettu lukumäärä, tulos täytetään oikealta \x00-tavuilla; jos pidempi, ylimääräiset tavut pudotetaan hiljaisesti:
struct.pack("4s", b"hi") # b'hi\x00\x00'
struct.pack("4s", b"toolong") # b'tool'
Lukumäärä on tavupituus, ei merkkien lukumäärä – s käsittelee raakatavuja, joten monitavuisia merkkejä sisältävä UTF-8-merkkijono pitää ensin .encode()-koodata ja laskea tavuina.
2.29.6. Koon laskenta ja osittaiset luvut¶
struct.calcsize() palauttaa tavujen määrän, jonka muotoilumerkkijono kuluttaa:
struct.calcsize("<HI") # 6
Kun luet tietueiden virtaa tiedostosta, lue täsmälleen tuo määrä tavuja kutakin tietuetta kohti:
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)
Tiedoston lopussa tapahtuva vajaa luku tuottaa record_size-arvoa pienemmän joukon – käsittele se virran lopun ehtona sen sijaan, että yrittäisit purkaa osittaisen tietueen.