2.6. Szöveg vs. bájtok#

A Pythonban két szekvenciatípus van a nyers karakteradatokhoz:

  • str – Unicode kódpontok sorozata. Minden ember által olvasható szöveghez használatos: fájlútvonalak, naplóüzenetek, JSON-adatok.

  • bytes – 0 – 255 tartományba eső egész számok sorozata. Nyers bináris adatokhoz használatos: UART-keretek, képpufferek, hálózati csomagok, regiszterértékek.

Explicit konverzió nélkül nem keverhetők. Ha egy str objektumot adsz át egy hardveres write metódusnak, TypeError kivétel keletkezik, és a fordítottja sem megengedett.

A str of Unicode codepoints on the left and a bytes sequence of raw octets on the right, with encode and decode arrows between them.

Egy str Unicode karaktereket tárol; egy bytes nyers oktetteket tárol. A kettő közötti átmenet a kódolás (str → bytes) és a dekódolás (bytes → str).#

2.6.1. bytes literálok#

A bytes literál egy karakterlánchoz hasonló literál, b előtaggal:

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

Egy bytes literálon belül közvetlenül csak ASCII karakterek engedélyezettek; a nem ASCII értékeket \xHH hexadecimális escape-ként kell írni.

2.6.2. Kódolás és dekódolás#

  • A str.encode() egy karakterláncot bájtokká alakít egy megnevezett kódolás (alapértelmezetten "utf-8") használatával.

  • A bytes.decode() ennek az ellentéte.

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

Az UTF-8 az alapértelmezett, és a helyes választás bármihez, ami nem ASCII karaktereket tartalmazhat. Az "ascii" kódolást csak akkor használd, ha az adat garantáltan sima ASCII; így egy elkóborolt nem ASCII bájt UnicodeError kivételt vált ki, ahelyett, hogy csendben átmenne.

2.6.3. Indexelés és szeletelés#

Egy bytes érték indexeléskor egész számok sorozataként viselkedik, nem pedig egybájtos karakterláncok sorozataként:

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

Gyakori hiba a data[0] == "a" összehasonlítása, és a meglepődés azon, hogy az eredmény False – a data[0] egy egész szám, nem pedig egykarakteres karakterlánc, így a két érték soha nem egyezhet.

2.6.4. ord és chr – híd a karakterek és az egész számok között#

Mivel egy bytes indexelése egész számot ad vissza, de a program többi része valószínűleg karakterekben gondolkodik, a Python két beépített függvényt biztosít a köztük való váltáshoz:

  • ord() – egy egykarakteres karakterláncot vesz, és visszaadja annak egész kódpontját.

  • chr() – ennek az ellentéte: egy egész számot kapva visszaadja az adott kódponthoz tartozó egykarakteres karakterláncot.

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

ASCII karakterek esetén a kódpont megegyezik a bájtértékkel, így az ord("a") és a b"a"[0] egyaránt 97-et ad. Ettől a bájt-összehasonlítások annak a karakternek a szempontjából olvashatók, amely valójában érdekel:

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

A chr() pedig hasznos naplózáshoz vagy hibakereséshez, amikor egy bájt nyomtatható alakját szeretnéd látni:

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

Nem ASCII karakterek esetén a ord() a Unicode kódpontot adja vissza, ami nem azonos a kódolt forma egyetlen bájtjával sem; a bájtábrázolás a kódolástól függ.

2.6.5. bytearray a módosítható pufferekhez#

A bytes megváltoztathatatlan – minden „módosítás” egy új objektumot ad vissza, és az eredetit érintetlenül hagyja. Olyan adatokhoz, amelyeket módosítani, hozzáfűzni vagy darabonként feltölteni szeretnél, használd a bytearray típust. Ugyanazt a tartalmat tárolja, mint a bytes, de támogatja a helyben történő módosítást:

>>> 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 létrehozása#

A bytearray konstruktor többféle bemenetet fogad el:

  • bytearray(8) – egy 8 nullbájtból álló puffer.

  • bytearray(b"hello") – egy bytes érték módosítható másolata.

  • bytearray("hello", "utf-8") – egy bytearray egy karakterláncból, az adott kódolás használatával.

  • bytearray([72, 73, 74]) – egy bytearray a 0 – 255 tartományba eső egész számok sorozatából (itt 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 módosítása#

Az indexelt és szeletelt értékadás pontosan úgy működik, mint egy list esetén:

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

Az egyes bájtoknak 0 – 255 tartományba eső egész számoknak kell lenniük; bármely más típus hozzárendelése TypeError vagy ValueError kivételt vált ki.

A szeletes értékadás megváltoztathatja a puffer hosszát. Egy szelet hosszabb értékkel való helyettesítése növeli a bytearray méretét; rövidebb értékkel való helyettesítése csökkenti azt. A b"" értékkel való helyettesítés teljesen törli a szeletet:

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

A bytearray.append() és bytearray.extend() metódusok bájtokat adnak hozzá a végéhez anélkül, hogy minden alkalommal újrafoglalnák a teljes puffert:

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

2.6.5.3. Olvasás egy bytearray-ből#

Az indexelés, a szeletelés, az iterálás és a bytes vizsgálati metódusai (bytes.startswith(), bytes.find(), bytes.strip() stb.) mind ugyanúgy működnek, mint egy bytes érték esetén. Az indexelés egy egész számot ad vissza; a szeletelés egy újabb bytearray-t ad vissza:

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

2.6.5.4. Konverzió a bytes és a bytearray között#

A bytes és a bytearray a konstruktoraikkal alakíthatók át egymásba. Ezt akkor használd, amikor egy API kifejezetten az egyik formát igényli:

>>> 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 a másolatmentes szeleteléshez#

Egy bytes vagy bytearray szeletelése normál esetben a bájtokat egy új pufferbe másolja. A memoryview ugyanazokat a bájtokat teszi elérhetővé másolás nélkül:

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

Egy bytearray feletti nézet írható is – a nézet módosítása módosítja az alapul szolgáló puffert:

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

Akkor nyúlj a memoryview típushoz, amikor egy szelet másolása pazarló lenne – jellemzően amikor ugyanazt a nagy puffert adogatják körbe vagy darabonként dolgozzák fel. A kis bájtokon végzett mindennapi, karakterlánc-stílusú munkához a sima szeletelés megfelelő.