Python typy danych bytes i str
Python

Konwersja typu bytes na str w Pythonie

W artykule znajdziesz informacje, w jaki sposób przekonwertować typ bytes na typ str w Pythonie.

Informacje na temat kodowania znaków i stron kodowych znajdziesz artykule Kodowanie znaków w plikach tekstowych

Typy danych w Pythonie

W języku Python mamy do dyspozycji następujące wbudowane typy danych:

  • numeryczne (liczbowe): liczby całkowite, liczby rzeczywiste
  • tekstowe (string): str
  • sekwencji: list, tuple
  • odwzorowania (mapping type): dict
  • zestawów (set types): set, frozenset
  • logiczne: bool
  • binarne: bytes, bytearray

W/w typy danych są wbudowane i możemy ich używać bez konieczności importowania zewnętrznej biblioteki.

Więcej informacji na temat typów danych znajdziesz w artykule Python typy danych

W Pythonie wartości określonego typu np. str lub bytes najczęściej przechowujemy w zmiennych. Typ zmiennej zależy od typu przechowywanej w niej wartości. Zmienna przyjmuje taki typ danych, jakiego typu wartość jest w niej przechowywana. Oznacza to, że:

  • jeśli do zmiennej przypiszemy dane typu str, zmienna będzie typu str
  • jeśli do zmiennej przypiszemy liczbę zmiennoprzecinkową (float), zmienna przyjmie typ float

Więcej informacji na temat zmiennych znajdziesz w artykule Python zmienne

Zmienne typu str i bytes

Zmienne typu str służą do przechowywania różnego typu ciągów tekstowych zwanych też łańcuchami tekstowymi.

Ciąg tekstowy jest sekwencją znaków – liter, cyfr, znaków przestankowych i sterujących (spacja, znak przejścia do nowej linii) np. „Pies Wojciecha waży 5 kg”.

Zmienne typu bytes służą do przechowywania ciągów bajtów.

Bajt to liczba całkowita z zakresu 0..255.

Typ str i typ bytes w Pythonie są bardzo podobne. Typ bytes w przeciwieństwie do typu str reprezentuje dane binarne, takie jak obrazy, dźwięki, pliki binarne.

Wartości typów str i bytes tworzymy używając:

  • str – apostrof ’ lub cudzysłów „
  • bytes – b apostrof b’ lub b cudzysłów b”

W poniższym przykładzie:

  • do zmiennej s przypisujemy słowo Hello i używamy typu str
  • do zmiennej b przypisujemy słowo Hello i używamy typu bytes. Każda litera reprezentuje jeden bajt, używamy kodowania ASCII
s = 'Hello str'
b = b'Hello bytes'

print(s) #wynik: Hello str
print(b) #wynik: b'Hello bytes'

Aby przypisać wartości do zmiennych, możemy też użyć cudzysłowów, potrójnych apostrofów lub potrójnych cudzysłowów.

s = """Hello str"""
b = b"""Hello bytes"""

print(s) # Hello str
print(b) # b'Hello bytes'

Znak ucieczki (escape)

W języku Python znak ucieczki jest znakiem specjalnym, który służy do zmiany znaczenia następującego po nim znaku w ciągu znaków. Na przykład, znak ucieczki przed cudzysłowem, powoduje, że Python traktuje cudzysłów jako część łańcucha znaków, a nie jako znak końca łańcucha.

Obydwa typy str i bytes wspierają znaki ucieczki (escape). Na przykład:

  • nowa linia: \n
  • tabulator: \t
  • znak ascii: \x??, gdzie ?? to kod ASCII znaku zapisany szesnastkowo
s = 'Hello str\x40\n'
b = b'Hello bytes\x40\n'

print(s) # Hello str@
print(b) # b'Hello bytes@\n'

Możemy też użyć modyfikatora r aby ignorować escape ze znakiem \\.

s = r'c:\Users\Robert\.ssh\id_dsa'
b = rb'c:\Users\Robert\.ssh\id_dsa'

print(s) # c:\Users\Robert\.ssh\id_dsa
print(b) # c:\Users\Robert\.ssh\id_dsa

Modyfikator f

Modyfikator f w Pythonie to sposób formatowania ciągów znaków, za pomocą którego możemy osadzić wartości zmiennych bezpośrednio wewnątrz łańcucha znaków. Jest to tzw. f-string, który jest dostępny od wersji Pythona 3.6.

Aby utworzyć f-string w Pythonie, należy przed ciągiem znaków dodać literę „f” lub „F”, a następnie wewnątrz łańcucha znaków umieścić wyrażenie, które ma zostać osadzone wewnątrz nawiasów klamrowych „{}”.

name = "John"
age = 30
message = f"My name is {name} and I am {age} years old."
print(message)

Dla typu danych bytes nie możemy używać modyfikatora f – formatowanego tekstu.

no = 3.1415
s = f'liczba {no}' # 'liczba 3.1415'
b = fb'liczba {no}' # SyntaxError: invalid syntax

W poniższym przykładzie uzyskujemy wartości bytes z listy liczb całkowitych (wartość z zakresu 0..255 włącznie):

b = bytes([3, 0, 4, 65, 145, 255])
print(b) # b'\x03\x00\x04A\x91\xff'

Wartość str może zawierać w środku zera (zero nie służy do sygnalizowania końca tekstu tak, jak w innych językach np. C):

s = '\x03\x00\x04A\x91\xff'

Metoda encode()

W Pythonie typ bytes może przechowywać teksty, ale tylko w postaci zakodowanej w sposób bajtowy. Oznacza to, że ciąg znaków musi być zakodowany w ASCII.

Jeśli ciąg znaków jest zakodowany np. w UTF-8, przed przypisaniem go do typu bytes musimy przekształcić go na sekwencję bajtów. Możemy to zrobić za pomocą metody encode() na obiekcie typu str (ciągu znaków).

Metoda encode() jest używana do kodowania łańcucha znaków (str) do postaci bajtowej (bytes) przy użyciu określonego kodowania znaków. Metoda ta zwraca obiekt typu bytes.

Metoda encode() przyjmuje jako argument kodowanie (ang. encoding), które określa sposób kodowania tekstu na bajty. Najczęściej używanym kodowaniem w Pythonie jest UTF-8, które obsługuje większość języków i zestawów znaków. Innymi popularnymi kodowaniami są np. ASCII, ISO-8859-1, cp1250.

Przykład użycia metody encode():

text = "Hello, World!"
bytes_text = text.encode('UTF-8')
print(bytes_text)

W tym przykładzie zmienna „text” zawiera łańcuch znaków „Hello, World!”. Następnie metoda encode() jest wywoływana na tym łańcuchu z argumentem UTF-8, co przekształca go na postać bajtową, która jest przypisana do zmiennej „bytes_text”. W wyniku tego wywołania otrzymujemy obiekt bytes reprezentujący tekst w postaci bajtowej: b’Hello, World!’.

Jeśli nie podamy żadnego kodowania, metoda encode() użyje domyślnego kodowania (zwykle UTF-8) i zwróci zakodowany tekst w postaci bajtowej.

text = "Hello, World!"
bytes_text = text.encode()
print(bytes_text)
[python]

Zapis do pliku

Pliki mogą być otwierane w dwóch trybach: 

<ul>
<li>tekstowym - można zapisać tylko wartości str</li>
<li>binarnym - można zapisać tylko wartości bytes</li>
</ul>

[python]
with open('plik.txt', 'w') as f:
    f.write('napis str')

with open('plik.txt', 'wb') as f:
    f.write(b'napis str')

Aby do pliku tekstowego zapisać str, można przekonwertować str na bytes funkcją encode().

Funkcja encode() zamienia ciąg tekstowy str na ciąg bajtów bytes.

with open('plik.txt', 'wb') as f:
    f.write('napis str'.encode())

Aby zamienić bytes na str używamy funkcji decode().

b = b'Hello bytes\x40\n'
s = b.decode()
print(s) # Hello bytes@

Jeśli wszystkie znaki pochodzą ze zbioru znaków ASCII, każdy znak będzie zamieniony na jeden bajt.

Znaki poza ASCII

Znaki, które nie występują w zestawie ASCII nie mogą być użyte w ciągu typu bytes jako składowe. Na przykład znak ℃ (stopnie Celcjusza) o kodzie 8451 jest nielegalny w ciągu bajtów:

s = '℃' # ok
print(len(s))  # 1
print(ord(s))  # 8451
b = b'℃' # SyntaxError: bytes can only contain ASCII literal characters.

Zamiana ciągu z tą tylko literą na bajty tworzy ciąg trzech bajtów, które można wyświetlić jako bytes a także jako listę:

s = '℃' # ok
print(s.encode()) # b'\xe2\x84\x83'
print(list(s.encode())) # [226, 132, 131]
print(len(s.encode())) # 3

Długość ciągu znaków wynosiła jeden, ale długość odpowiadającego jej ciągu bajtów wynosi 3. Ten ciąg bajtów jednoznacznie reprezentuje dany ciąg tekstowy. Możliwa jest więc operacja odwrotna, zamiany bajtów bytes na ciąg str.

lista = [226, 132, 131]
b = bytes(lista)
print(b.decode())  # ℃

To, w jaki sposób poszczególne znaki są zamieniane na bajty i jak bajty na znaki jest zdefiniowane przez encoding.

Encoding – funkcje encode i decode

Encoding to mapowanie jaki znak to jaki bajt lub sekwencja bajtów. Każdy znak może być reprezentowany w bajtach przez jeden, dwa lub więcej bajtów. Domyślne kodowanie dla funkcji encode() i decode() to UTF-8 (Unicode Transformation Format) . UTF-8 to najpopularniejsze kodowanie znaków. Inne kodowania są używane tylko w specyficznych zastosowaniach lub z powodów historycznych. Przy odczycie i zapisie do pliku domyślne kodowanie jest odczytywane z systemu i niekoniecznie musi to być utf-8.

with open('plik.txt', 'w') as f:
    f.write('napis str')

Najlepiej w funkcji open() zawsze określać pożądany encoding podając parametr encoding=’utf-8′:

with open('plik.txt', 'w', encoding='utf-8') as f:
    f.write('napis str')

Użycie niewłaściwego kodowania przy odczycie danych może wygenerować błąd konwersji (błąd odczytu), albo może wygenerować błędne dane tekstowe bez żadnego ostrzeżenia.

with open('plik.txt', 'w', encoding='utf-8') as f:
    f.write('Liście lecą z drzew')

with open('plik.txt', 'r', encoding='cp1250') as f:
    s = f.read()
print(s) # Liście lecą z drzew

Odczytując plik tekstowy musimy poprawnie ustawić kodowanie, w przeciwnym wypadku plik może się bez ostrzeżenia niepoprawnie odczytać. Musimy z góry wiedzieć jakie kodowanie zostało użyte przy tworzeniu pliku tekstowego.

Odczyt tekstu z Internetu

O kodowaniu znaków musimy pamiętać pobierając dane tekstowe z Internetu. Dotyczy to tylko danych tekstowych, gdyż jeśli pobieramy dane binarne (np. pdf, png, mp3) kodowanie nie ma zastosowania.

Moduł urllib.request

Do pobierania danych z Internetu możemy użyć wbudowanego modułu urllib.request. Funkcja urlopen() otwiera strumień binarny danych z Internetu, który zostaje zapisany w zmiennej f. Następnie funkcja read() odczytuje dane w postaci bajtów bytes do zmiennej b.

from urllib.request import urlopen

url='http://httpbin.org/get'
with urlopen(url) as f:
    b = f.read()
print(b) # b'{\n  "args": {}, \n  "headers": {\n'

Do konwersji na str posłużymy się funkcją decode() i domyślnym kodowaniem czyli utf-8:

from urllib.request import urlopen

# wczytaj dane z Internetu
url='http://httpbin.org/get'
with urlopen(url) as f:
    b = f.read()
    
# zamień wczytane dane bytes na str
s = b.decode() # domyślnie: utf-8
print(s)
# {
#   "args": {}, 
#   "headers": {

Jeśli pobieramy plik binarny, rezultat pozostawimy jako bytes:

from urllib.request import urlopen
url='https://shattered.io/static/infographic.pdf'
with urlopen(url) as f:
    b = f.read()

print(b[:10]) # b'%PDF-1.3\n%'  

Moduł requests

Moduł requests jest nowocześniejszy, ale nie jest standardowo włączony do Pythona i wymaga instalacji programem pip.

Funkcja get() pobiera dane w postaci obiektu response. Aby odczytać pobrane dane jako tekst, ustawiam aktywne kodowanie na utf-8 i odczytuję właściwość text już jako przekonwertowany typ str.

import requests # pip install requests

url='http://httpbin.org/get'
response = requests.get(url) # pobierz dane

response.encoding = 'utf-8' # użyj kodowania utf-8 
s = response.text # odczytaj zawartość jako str
print(s)

Odczytanie danych binarnych nie wymaga dekodowania, używamy wtedy właściwości content:

import requests
url='https://shattered.io/static/infographic.pdf'
b = requests.get(url).content
print(b[:10]) # b'%PDF-1.3\n%'  

Podsumowanie

Kodowanie znaków ASCII przypisuje każdej literze i znakowi odpowiadający bajt. Jeśli używane są tylko znaki Ascii, kodowanie utf-8 używa dokładnie tych samych bajtów co kodowanie Ascii. Do zakodowania polskich znaków w UTF-8 używamy dwóch bajtów.

Rafał Lelusz

Programista Python, C#
Udostępnij wpis: udostępnij Facebook udostępnij Linkedin udostępnij e-mail

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

    Podobne artykuły z kategorii: Python

    Może Cię zainteresować