2.6. Text kontra bytes¶
Python har två sekvenstyper för rådata av tecken:
str– en sekvens av Unicode-kodpunkter. Används för all läsbar text: filsökvägar, loggmeddelanden, JSON-nyttolaster.bytes– en sekvens av heltal i intervallet 0 – 255. Används för rå binärdata: UART-bildrutor, bildbuffertar, nätverkspaket, registervärden.
De kan inte blandas utan en explicit konvertering. Att skicka en str till en hårdvarumetod write ger upphov till TypeError, och det omvända avvisas också.
En str lagrar Unicode-tecken; en bytes lagrar rå oktetter. Att gå mellan dem är kodning (str → bytes) och avkodning (bytes → str).¶
2.6.1. bytes-literaler¶
En bytes-literal är en stränglik literal prefixad med b:
header = b"OMV"
crlf = b"\r\n"
payload = b"\x01\x02\x03"
Endast ASCII-tecken tillåts direkt inuti en bytes-literal; värden som inte är ASCII måste skrivas som \xHH-hexescaper.
2.6.2. Kodning och avkodning¶
str.encode()konverterar en sträng till bytes med hjälp av en namngiven kodning (standard"utf-8").bytes.decode()gör det omvända.
>>> "hello".encode()
b'hello'
>>> "héllo".encode()
b'h\xc3\xa9llo' # é is two bytes in UTF-8
>>> b"hello".decode()
'hello'
UTF-8 är standarden och rätt val för allt som kan innehålla tecken som inte är ASCII. Använd "ascii" endast när datan garanterat är ren ASCII; på så sätt ger en felaktig icke-ASCII-byte upphov till UnicodeError i stället för att tyst passera igenom.
2.6.3. Indexering och slicing¶
Ett bytes-värde beter sig som en sekvens av heltal vid indexering, inte som en sekvens av enbytsträngar:
>>> data = b"abc"
>>> data[0]
97 # the int 97, not 'a'
>>> data[0:1]
b'a' # slicing returns bytes
Ett vanligt misstag är att jämföra data[0] == "a" och bli förvånad över att det är False – data[0] är ett heltal, inte en sträng på ett tecken, så de två värdena kan aldrig matcha.
2.6.4. ord och chr – en brygga mellan tecken och heltal¶
Eftersom indexering av en bytes returnerar ett heltal men resten av programmet sannolikt tänker i termer av tecken, tillhandahåller Python två inbyggda funktioner för att gå mellan dem:
ord()– tar en sträng på ett tecken och returnerar dess heltalskodpunkt.chr()– det omvända: givet ett heltal returneras strängen på ett tecken för den kodpunkten.
>>> ord("a")
97
>>> chr(97)
'a'
>>> ord("A"), chr(0x41)
(65, 'A')
För ASCII-tecken är kodpunkten lika med byte-värdet, så ord("a") och b"a"[0] ger båda 97. Det gör att byte-jämförelser kan läsas i termer av det tecken du faktiskt bryr dig om:
>>> data = b"abc"
>>> data[0] == ord("a") # instead of the magic number 97
True
Och chr() är behändig för loggning eller felsökning när du vill se den utskrivbara formen av en byte:
>>> chr(data[0])
'a'
För tecken som inte är ASCII returnerar ord() Unicode-kodpunkten, vilken inte är densamma som någon enskild byte i den kodade formen; byte-representationen beror på kodningen.
2.6.5. bytearray för föränderliga buffertar¶
bytes är oföränderlig – varje ”ändring” returnerar ett nytt objekt och lämnar originalet orört. För data som du avser att ändra, lägga till i eller fylla bit för bit, använd bytearray. Den håller samma innehåll som bytes men stöder förändring på plats:
>>> 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. Skapa en bytearray¶
Konstruktorn bytearray tar emot flera olika indata:
bytearray(8)– en buffert med 8 nollbytes.bytearray(b"hello")– en föränderlig kopia av ett bytes-värde.bytearray("hello", "utf-8")– en bytearray från en sträng, med den angivna kodningen.bytearray([72, 73, 74])– en bytearray från en sekvens av heltal i 0 – 255 (härb"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. Modifiera en bytearray¶
Indexerad och slicad tilldelning fungerar precis som för en 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')
Enskilda bytes måste vara heltal i 0 – 255; att tilldela någon annan typ ger upphov till TypeError eller ValueError.
Slice-tilldelning kan ändra buffertens längd. Att ersätta en slice med ett längre värde gör att bytearrayen växer; att ersätta med ett kortare värde gör att den krymper. Att ersätta med b"" tar bort slicen helt:
>>> 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')
Metoderna bytearray.append() och bytearray.extend() lägger till bytes i slutet utan att omallokera hela bufferten varje gång:
>>> buf = bytearray()
>>> buf.append(0x01)
>>> buf.extend(b"abc")
>>> buf
bytearray(b'\x01abc')
2.6.5.3. Läsa från en bytearray¶
Indexering, slicing, iteration och bytes-inspektionsmetoderna (bytes.startswith(), bytes.find(), bytes.strip() osv.) fungerar alla på samma sätt som på ett bytes-värde. Indexering returnerar ett heltal; slicing returnerar en annan bytearray:
>>> buf = bytearray(b"OpenMV")
>>> buf[0]
79
>>> buf[0:4]
bytearray(b'Open')
>>> buf.startswith(b"Open")
True
2.6.5.4. Konvertera mellan bytes och bytearray¶
bytes och bytearray konverteras till varandra med sina konstruktorer. Använd detta när ett API specifikt kräver en av formerna:
>>> 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 för slicing utan kopiering¶
Att slica en bytes eller bytearray kopierar normalt sett byten in i en ny buffert. memoryview exponerar samma bytes utan att kopiera:
>>> 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'
En vy över en bytearray är också skrivbar – att förändra vyn förändrar den underliggande bufferten:
>>> view[0] = ord("o")
>>> buf
bytearray(b'openMV Cam')
Ta till memoryview när det skulle vara slöseri att kopiera en slice – vanligtvis när samma stora buffert skickas runt eller behandlas i delar. För vardaglig sträng-liknande hantering av små bytes räcker vanlig slicing gott.