Tekst, bytes i kodowanie UTF-8
Pliki przechowują bajty. Sposób, w jaki bajty są zamieniane na tekst, to kodowanie i nie jest to domyślnie uniwersalne.
Tekst i bytes to różne typy
print(type("Python")) # <class 'str'>
print(type(b"Python")) # <class 'bytes'>
bytes jest łańcuch znaków liczb całkowitych od 0 do 255 (dane binarne), a nie skróconą wersją łańcuch znaków. Tekst łańcuch znaków (str) oraz dane binarne (bytes) — dwa różne typy o różnym przeznaczeniu.Kodowanie i dekodowanie
Aby zapisać tekst w pliku, jego reprezentacja łańcuch znaków jest zamieniana na bajty — to jest jest nazywana kodowaniem (encode). Podczas czytania dzieje się odwrotnie — Dekodowanie (decode):
text = „Cześć”
b = text.encode("utf-8")
print(b) # b'\\xd0\\x9f\\xd1\\x80\\xd0\\xb8\\xd0\\xb2\\xd0\\xb5\\xd1\\x82'
print(len(text)) #6 – Sześć postaci
print(len(b)) #12 – dwanaście bajtów!
obratno = b.decode("utf-8")
print(obratno) #Hello
Gdy format tekstu jest znany wcześniej jako UTF-8 (co prawie zawsze ma miejsce na tym kursie),
Wyraźne wskazanie encoding="utf-8" wpływa na zachowanie programu
przewidywalny i przenośny – zamiast polegać na domyślnym kodowaniu,
co jest determinowane przez ustawienia platformy i środowiska wykonawczego.
Dlaczego kodowanie jest ważne
with open("privet.txt", "w") as f: # bez encoding!
f.write(„Cześć!”)
# Работает по-разному на разных компьютерах и системах —
# кодировка по умолчанию НЕ гарантированно UTF-8 везде.
Bez jawnego encoding="utf-8" Python używa kodowania domyślnego, zależnego od platformy i ustawień środowiska — a nie gwarantowanie UTF-8. Plik zapisany na jednym komputerze może być błędnie odczytany na innym, jeśli ich domyślne kodowania się różnią.
with open("privet.txt", "w", encoding="utf-8") as f:
f.write(„Cześć!”) # kodowanie jest jawne i przewidywalne przez cały czas
data = „Cześć”.encode("utf-8")
with open("soobshenie.bin", "wb") as f:
f.write(data)
# czytane jako tekst w INNYM kodowaniu
with open("soobshenie.bin", "r", encoding="ascii") as f:
print(f.read())
Traceback (most recent call last):
UnicodeDecodeError: 'ascii' codec can't decode byte 0xd0 in position 0: ordinal not in range(128)
bajty były kodowane w UTF-8 i odczytywane tak, jakby ASCII, kodowania nie pasowały i nie Python mógł przekształcić tych bajtów z powrotem w poprawne znaki. Rozwiązaniem jest odczytanie pliku w tym samym kodowaniu, w którym został zapisany.
with open("soobshenie.bin", "r", encoding="utf-8") as f:
print(f.read()) #Hello
Trochę głębiej: parametr errors=
U open() jest parametrem errors
("strict" też domyślnie "replace",
"ignore") w przypadkach niezgodności kodu.
errors="ignore" cicho wyrzuca nierozpoznane bajty — może to niezauważalnie spowodować utratę części danych. Nie używaj tego jako sposobu na „usunięcie błędu”