Текст, bytes и кодировка UTF-8
Файлы хранят байты. То, как байты превращаются в текст, — это кодировка, и она не универсальна по умолчанию.
Текст и bytes — разные типы
print(type("Python")) # <class 'str'>
print(type(b"Python")) # <class 'bytes'>
bytes — это последовательность целых чисел от 0 до 255 (двоичные данные), а не урезанная версия строки. Текстовая строка (str) и бинарные данные (bytes) — два разных типа с разным назначением.Кодирование и декодирование
Чтобы записать текст в файл, его строковое представление превращают в байты — это называется кодированием (encode). При чтении происходит обратное — декодирование (decode):
text = "Привет"
b = text.encode("utf-8")
print(b) # b'\\xd0\\x9f\\xd1\\x80\\xd0\\xb8\\xd0\\xb2\\xd0\\xb5\\xd1\\x82'
print(len(text)) # 6 — шесть символов
print(len(b)) # 12 — двенадцать байт!
obratno = b.decode("utf-8")
print(obratno) # Привет
Когда формат текста заранее известен как UTF-8 (а в этом курсе это так почти всегда),
явное указание encoding="utf-8" делает поведение программы
предсказуемым и переносимым — вместо того, чтобы зависеть от кодировки по умолчанию,
которая определяется платформой и настройками окружения выполнения.
Почему кодировка важна
with open("privet.txt", "w") as f: # без encoding!
f.write("Привет!")
# Работает по-разному на разных компьютерах и системах —
# кодировка по умолчанию НЕ гарантированно UTF-8 везде.
Без явного encoding="utf-8" Python использует кодировку по умолчанию, зависящую от платформы и настроек окружения — а не гарантированно UTF-8. Файл, записанный на одном компьютере, может быть неправильно прочитан на другом, если их кодировки по умолчанию различаются.
with open("privet.txt", "w", encoding="utf-8") as f:
f.write("Привет!") # кодировка указана явно и предсказуема всегда
data = "Привет".encode("utf-8")
with open("soobshenie.bin", "wb") as f:
f.write(data)
# читаем как текст в ДРУГОЙ кодировке
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)
Байты были закодированы в UTF-8, а прочитаны как будто в ASCII — кодировки не совпали, и Python не смог превратить эти байты обратно в корректные символы. Решение — читать файл в той же кодировке, в которой он был записан.
with open("soobshenie.bin", "r", encoding="utf-8") as f:
print(f.read()) # Привет
Чуть глубже: параметр errors=
У open() есть параметр errors
("strict" по умолчанию, также "replace",
"ignore") для случаев несовпадения кодировки.
errors="ignore" молча выбрасывает нераспознанные байты — это может незаметно потерять часть данных. Не используйте его как способ «просто убрать ошибку» без понимания, что именно теряется.