2.6. Teksti vs. tavut¶
Pythonissa on kaksi jonotyyppiä raakamerkkidatalle:
str– Unicode-koodipisteiden jono. Käytetään kaikkeen ihmisen luettavissa olevaan tekstiin: tiedostopolkuihin, lokiviesteihin, JSON-kuormiin.bytes– kokonaislukujen jono alueella 0 – 255. Käytetään raakaan binääridataan: UART-kehyksiin, kuvapuskureihin, verkkopaketteihin, rekisteriarvoihin.
Niitä ei voi sekoittaa keskenään ilman eksplisiittistä muunnosta. str-arvon välittäminen laitteiston write-metodille nostaa poikkeuksen TypeError, ja päinvastainenkin hylätään.
str tallentaa Unicode-merkkejä; bytes tallentaa raakoja oktetteja. Niiden välillä siirtyminen on koodausta (str → bytes) ja dekoodausta (bytes → str).¶
2.6.1. bytes-literaalit¶
bytes-literaali on merkkijonon kaltainen literaali, jonka edessä on b:
header = b"OMV"
crlf = b"\r\n"
payload = b"\x01\x02\x03"
bytes-literaalin sisällä sallitaan suoraan vain ASCII-merkit; ei-ASCII-arvot on kirjoitettava \xHH-heksasuojauksina.
2.6.2. Koodaus ja dekoodaus¶
str.encode()muuntaa merkkijonon tavuiksi käyttäen nimettyä koodausta (oletuksena"utf-8").bytes.decode()tekee käänteisen muunnoksen.
>>> "hello".encode()
b'hello'
>>> "héllo".encode()
b'h\xc3\xa9llo' # é is two bytes in UTF-8
>>> b"hello".decode()
'hello'
UTF-8 on oletus ja oikea valinta kaikelle, mikä saattaa sisältää ei-ASCII-merkkejä. Käytä "ascii" vain silloin, kun data on taatusti pelkkää ASCIIa; tällöin yksittäinen ei-ASCII-tavu nostaa poikkeuksen UnicodeError sen sijaan, että se menisi hiljaisesti läpi.
2.6.3. Indeksointi ja viipalointi¶
bytes-arvo käyttäytyy indeksoitaessa kokonaislukujen jonona, ei yhden tavun mittaisten merkkijonojen jonona:
>>> data = b"abc"
>>> data[0]
97 # the int 97, not 'a'
>>> data[0:1]
b'a' # slicing returns bytes
Yleinen virhe on verrata data[0] == "a" ja yllättyä siitä, että tulos on False – data[0] on kokonaisluku, ei yhden merkin merkkijono, joten kaksi arvoa eivät voi koskaan täsmätä.
2.6.4. ord ja chr – silta merkkien ja kokonaislukujen välillä¶
bytes-arvon indeksointi palauttaa kokonaisluvun, mutta muu ohjelma ajattelee todennäköisesti merkkeinä, joten Python tarjoaa kaksi sisäänrakennettua funktiota niiden välillä liikkumiseen:
ord()– ottaa yhden merkin merkkijonon ja palauttaa sen kokonaislukukoodipisteen.chr()– käänteinen: annetusta kokonaisluvusta se palauttaa kyseisen koodipisteen yhden merkin merkkijonon.
>>> ord("a")
97
>>> chr(97)
'a'
>>> ord("A"), chr(0x41)
(65, 'A')
ASCII-merkeillä koodipiste on sama kuin tavun arvo, joten sekä ord("a") että b"a"[0] antavat tuloksen 97. Tämä tekee tavuvertailuista luettavia siltä kannalta, mikä merkki sinua todella kiinnostaa:
>>> data = b"abc"
>>> data[0] == ord("a") # instead of the magic number 97
True
chr() on kätevä lokitukseen tai virheenetsintään, kun haluat nähdä tavun tulostettavan muodon:
>>> chr(data[0])
'a'
Ei-ASCII-merkeillä ord() palauttaa Unicode-koodipisteen, joka ei ole sama kuin mikään yksittäinen tavu koodatussa muodossa; tavuesitys riippuu koodauksesta.
2.6.5. bytearray muokattavia puskureita varten¶
bytes on muuttumaton – jokainen ”muokkaus” palauttaa uuden olion ja jättää alkuperäisen koskemattomaksi. Dataa varten, jota aiot muokata, lisätä siihen tai täyttää pala kerrallaan, käytä bytearray. Se sisältää saman sisällön kuin bytes, mutta tukee paikallaan tapahtuvaa muokkausta:
>>> s = b"hello"
>>> s[0] = ord("H")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'bytes' object does not support item assignment
>>> s = bytearray(b"hello")
>>> s[0] = ord("H")
>>> s
bytearray(b'Hello')
2.6.5.1. bytearray-olion luominen¶
bytearray-konstruktori hyväksyy useita syötteitä:
bytearray(8)– 8 nollatavun puskuri.bytearray(b"hello")– muokattava kopio bytes-arvosta.bytearray("hello", "utf-8")– bytearray merkkijonosta annettua koodausta käyttäen.bytearray([72, 73, 74])– bytearray kokonaislukujen jonosta alueella 0 – 255 (tässäb"HIJ").
>>> bytearray(4)
bytearray(b'\x00\x00\x00\x00')
>>> bytearray(b"abc")
bytearray(b'abc')
>>> bytearray("café", "utf-8")
bytearray(b'caf\xc3\xa9')
2.6.5.2. bytearray-olion muokkaaminen¶
Indeksoitu ja viipaloitu sijoitus toimivat aivan kuten list-tyypissä:
>>> buf = bytearray(8) # 8 zero bytes
>>> buf[0] = 0xFF # one byte at a time
>>> buf[1:4] = b"ABC" # replace a slice
>>> buf
bytearray(b'\xffABC\x00\x00\x00\x00')
Yksittäisten tavujen on oltava kokonaislukuja alueella 0 – 255; minkä tahansa muun tyypin sijoittaminen nostaa poikkeuksen TypeError tai ValueError.
Viipalesijoitus voi muuttaa puskurin pituutta. Viipaleen korvaaminen pidemmällä arvolla kasvattaa bytearray-oliota; korvaaminen lyhyemmällä arvolla pienentää sitä. Korvaaminen arvolla b"" poistaa viipaleen kokonaan:
>>> buf = bytearray(b"abcdef")
>>> buf[1:3] = b"XYZ" # 2 bytes replaced with 3
>>> buf
bytearray(b'aXYZdef')
>>> buf[1:4] = b"" # delete the inserted run
>>> buf
bytearray(b'adef')
Metodit bytearray.append() ja bytearray.extend() lisäävät tavuja loppuun ilman, että koko puskuria varataan joka kerta uudelleen:
>>> buf = bytearray()
>>> buf.append(0x01)
>>> buf.extend(b"abc")
>>> buf
bytearray(b'\x01abc')
2.6.5.3. bytearray-oliosta lukeminen¶
Indeksointi, viipalointi, iterointi ja bytes-tarkastelumetodit (bytes.startswith(), bytes.find(), bytes.strip() jne.) toimivat kaikki samalla tavalla kuin bytes-arvossa. Indeksointi palauttaa kokonaisluvun; viipalointi palauttaa toisen bytearray-olion:
>>> buf = bytearray(b"OpenMV")
>>> buf[0]
79
>>> buf[0:4]
bytearray(b'Open')
>>> buf.startswith(b"Open")
True
2.6.5.4. Muuntaminen bytes- ja bytearray-tyyppien välillä¶
bytes ja bytearray muunnetaan toisikseen konstruktoreillaan. Käytä tätä, kun rajapinta vaatii nimenomaan tiettyä muotoa:
>>> ba = bytearray(b"hello")
>>> snapshot = bytes(ba) # immutable copy
>>> ba[0] = ord("H")
>>> ba, snapshot
(bytearray(b'Hello'), b'hello')
2.6.5.5. memoryview kopioimatonta viipalointia varten¶
bytes- tai bytearray-arvon viipalointi kopioi tavut yleensä uuteen puskuriin. memoryview paljastaa samat tavut kopioimatta niitä:
>>> buf = bytearray(b"OpenMV Cam")
>>> view = memoryview(buf)
>>> view[0:6] # shares storage with buf
<memoryview ...>
>>> bytes(view[0:6]) # materialise as bytes when needed
b'OpenMV'
bytearray-olion näkymä on myös kirjoitettavissa – näkymän muokkaaminen muokkaa taustalla olevaa puskuria:
>>> view[0] = ord("o")
>>> buf
bytearray(b'openMV Cam')
Turvaudu memoryview-luokkaan, kun viipaleen kopiointi olisi tuhlausta – tyypillisesti silloin, kun samaa suurta puskuria kuljetetaan ympäriinsä tai käsitellään palasittain. Pienillä tavuilla tehtävässä jokapäiväisessä merkkijonotyylisessä työssä tavallinen viipalointi riittää.