2.6. Tekst nasuprot bajtovima

Python ima dva sekvencijska tipa za sirove znakovne podatke:

  • str – niz Unicode kodnih točaka. Koristi se za sav tekst čitljiv ljudima: putanje datoteka, poruke dnevnika, JSON sadržaje.

  • bytes – niz cijelih brojeva u rasponu 0 – 255. Koristi se za sirove binarne podatke: UART okvire, međuspremnike slike, mrežne pakete, vrijednosti registara.

Ne mogu se miješati bez eksplicitne konverzije. Prosljeđivanje str hardverskoj write metodi podiže TypeError, a i obrnuto se odbija.

Str Unicode kodnih točaka s lijeve strane i bytes niz sirovih okteta s desne strane, sa strelicama encode i decode između njih.

str pohranjuje Unicode znakove; bytes pohranjuje sirove oktete. Prelazak između njih je kodiranje (str → bytes) i dekodiranje (bytes → str).

2.6.1. bytes literali

bytes literal je literal sličan stringu, prefiksiran s b:

header  = b"OMV"
crlf    = b"\r\n"
payload = b"\x01\x02\x03"

Samo ASCII znakovi dopušteni su izravno unutar bytes literala; vrijednosti koje nisu ASCII moraju se napisati kao \xHH heksadecimalni escapeovi.

2.6.2. Kodiranje i dekodiranje

>>> "hello".encode()
b'hello'
>>> "héllo".encode()
b'h\xc3\xa9llo'              # é is two bytes in UTF-8
>>> b"hello".decode()
'hello'

UTF-8 je zadani izbor i pravi izbor za sve što bi moglo sadržavati znakove koji nisu ASCII. Koristite "ascii" samo kada je zajamčeno da su podaci čisti ASCII; na taj način slučajni bajt koji nije ASCII podiže UnicodeError umjesto da tiho prođe.

2.6.3. Indeksiranje i rezanje

bytes vrijednost ponaša se kao niz cijelih brojeva kada se indeksira, a ne kao niz stringova od jednog bajta:

>>> data = b"abc"
>>> data[0]
97                           # the int 97, not 'a'
>>> data[0:1]
b'a'                         # slicing returns bytes

Česta pogreška je usporedba data[0] == "a" i iznenađenje što je Falsedata[0] je cijeli broj, a ne string od jednog znaka, pa se te dvije vrijednosti nikada ne mogu podudarati.

2.6.4. ord i chr – premošćivanje znakova i cijelih brojeva

Budući da indeksiranje bytes vraća cijeli broj, ali ostatak programa vjerojatno razmišlja u znakovima, Python pruža dvije ugrađene funkcije za prelazak između njih:

  • ord() – uzima string od jednog znaka i vraća njegovu cjelobrojnu kodnu točku.

  • chr() – obrnuto: za zadani cijeli broj vraća string od jednog znaka za tu kodnu točku.

>>> ord("a")
97
>>> chr(97)
'a'
>>> ord("A"), chr(0x41)
(65, 'A')

Za ASCII znakove kodna točka jednaka je vrijednosti bajta, pa ord("a") i b"a"[0] oboje daju 97. To omogućuje da se usporedbe bajtova čitaju u smislu znaka do kojeg vam je zapravo stalo:

>>> data = b"abc"
>>> data[0] == ord("a")          # instead of the magic number 97
True

A chr() je koristan za bilježenje ili otkrivanje pogrešaka kada želite vidjeti ispisivi oblik bajta:

>>> chr(data[0])
'a'

Za znakove koji nisu ASCII ord() vraća Unicode kodnu točku, koja nije ista kao bilo koji pojedinačni bajt u kodiranom obliku; reprezentacija bajtova ovisi o kodiranju.

2.6.5. bytearray za promjenjive međuspremnike

bytes je nepromjenjiv – svaka „izmjena” vraća novi objekt i ostavlja izvornik nepromijenjen. Za podatke koje namjeravate mijenjati, dodavati im ili ih popunjavati dio po dio, koristite bytearray. Sadrži isti sadržaj kao bytes, ali podržava mutaciju na mjestu:

>>> 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. Stvaranje bytearraya

Konstruktor bytearray prihvaća nekoliko ulaza:

  • bytearray(8) – međuspremnik od 8 nula-bajtova.

  • bytearray(b"hello") – promjenjiva kopija bytes vrijednosti.

  • bytearray("hello", "utf-8") – bytearray iz stringa, koristeći zadano kodiranje.

  • bytearray([72, 73, 74]) – bytearray iz niza cijelih brojeva u rasponu 0 – 255 (ovdje 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. Mijenjanje bytearraya

Indeksirano i isječno pridruživanje rade jednako kao kod list:

>>> 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')

Pojedinačni bajtovi moraju biti cijeli brojevi u rasponu 0 – 255; pridruživanje bilo kojeg drugog tipa podiže TypeError ili ValueError.

Isječno pridruživanje može promijeniti duljinu međuspremnika. Zamjena isječka duljom vrijednošću proširuje bytearray; zamjena kraćom vrijednošću ga smanjuje. Zamjena s b"" u potpunosti briše isječak:

>>> 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')

Metode bytearray.append() i bytearray.extend() dodaju bajtove na kraj bez ponovne alokacije cijelog međuspremnika svaki put:

>>> buf = bytearray()
>>> buf.append(0x01)
>>> buf.extend(b"abc")
>>> buf
bytearray(b'\x01abc')

2.6.5.3. Čitanje iz bytearraya

Indeksiranje, rezanje, iteracija i metode za pregled bytes (bytes.startswith(), bytes.find(), bytes.strip() itd.) rade jednako kao na bytes vrijednosti. Indeksiranje vraća cijeli broj; rezanje vraća drugi bytearray:

>>> buf = bytearray(b"OpenMV")
>>> buf[0]
79
>>> buf[0:4]
bytearray(b'Open')
>>> buf.startswith(b"Open")
True

2.6.5.4. Konverzija između bytes i bytearray

bytes i bytearray pretvaraju se jedan u drugi pomoću svojih konstruktora. Koristite to kada API zahtijeva jedan oblik posebno:

>>> 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 za rezanje bez kopiranja

Rezanje bytes ili bytearray obično kopira bajtove u novi međuspremnik. memoryview izlaže iste bajtove bez kopiranja:

>>> 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'

Pogled nad bytearray također je zapisiv – mutacija pogleda mutira osnovni međuspremnik:

>>> view[0] = ord("o")
>>> buf
bytearray(b'openMV Cam')

Posegnite za memoryview kada bi kopiranje isječka bilo rasipno – obično kada se isti veliki međuspremnik prosljeđuje uokolo ili obrađuje u dijelovima. Za svakodnevni rad sa stringovima na malim bajtovima, obično rezanje je u redu.