Python: работа с кодировками. Методы, ошибки, преобразования

Раздел: Python -> Кодировки

В Python работа с текстом неразрывно связана с понятием кодировок. Строковый тип str хранит данные в Unicode, а байтовый тип bytes представляет их в виде последовательности чисел, интерпретированных согласно определенной кодировке. Для перехода между этими двумя представлениями используются методы encode() и decode(). Далее рассмотрены основные приемы и часто встречающиеся проблемы.

Основные методы encode() и decode() с кодировкой UTF-8

Наиболее распространенная кодировка в современных приложениях – UTF-8. Она поддерживает все символы Unicode и является кодировкой по умолчанию во многих средах.


# Кодирование строки в UTF-8 (получаем bytes)
text = "Привет, мир!"
encoded = text.encode('utf-8')
print(encoded)  # b'\xd0\x9f\xd1\x80...'

# Декодирование обратно в строку
decoded = encoded.decode('utf-8')
print(decoded)  # Привет, мир!

Python bytes encoding (кодирование байтов в python)

Если при декодировании встретятся байты, недопустимые для указанной кодировки, возникнет UnicodeDecodeError. Решить это можно параметром errors.


bad_bytes = b'\xff\xfe'
try:
    bad_bytes.decode('utf-8')
except UnicodeDecodeError as e:
    print(f"Ошибка: {e}")

# Использование replace - замена проблемных байтов на символ '?'
result = bad_bytes.decode('utf-8', errors='replace')
print(result)  # ??

# Использование ignore - пропуск проблемных байтов
result = bad_bytes.decode('utf-8', errors='ignore')
print(result)  # (пустая строка)

Encoding decoding python (кодирование и декодирование в python)

Типичная ошибка: UnicodeDecodeError при чтении файла

При открытии файла без указания кодировки Python использует системную кодировку, которая может не совпадать с фактической. Это приводит к ошибке. Решение – явно указывать кодировку при открытии.

Как закодировать строку в ASCII, пропуская символы, которые нельзя представить?

Для кодирования в ASCII с игнорированием недопустимых символов используется errors='ignore'. Это приводит к потере информации, но позволяет избежать исключения.


rus = "Привет"
ascii_ignored = rus.encode('ascii', errors='ignore')
print(ascii_ignored)  # b'' (все символы потеряны)

Encoding python (кодировки в python)

Проблема: полная потеря символов, не входящих в ASCII. Более безопасный вариант – использовать errors='replace' (замена на '?') или errors='xmlcharrefreplace' (замена на HTML-сущности).


print(rus.encode('ascii', errors='xmlcharrefreplace'))
# b'Привет'

Python 2 encoding (кодировки в python 2)

Что делать, если декодирование байтов вызывает UnicodeDecodeError?

Если невозможно заранее узнать кодировку, можно попробовать несколько распространенных кодировок (CP1251, Latin-1, KOI8-R и т.д.) или использовать модуль chardet для автоматического определения. В качестве временного решения применяют errors='replace'.


data = b'\xd0\x9f\xd1\x80\xdd'  # последний байт некорректен для UTF-8
print(data.decode('utf-8', errors='replace'))  # Пр?  (замена на ?)

Python encode (метод encode в python)

Ошибка: символы замены искажают исходные данные. Для критически важных данных следует обеспечить правильную кодировку на этапе получения данных.

Как правильно открыть текстовый файл в нужной кодировке?

При открытии файла в текстовом режиме следует передать аргумент encoding. Это гарантирует корректное преобразование байтов в строки и обратно.


# Запись в файл в кодировке UTF-8
with open('example.txt', 'w', encoding='utf-8') as f:
    f.write('Привет, файл!')

# Чтение файла в кодировке CP1251
with open('example.txt', 'r', encoding='cp1251') as f:
    content = f.read()  # может быть искажено, если файл в UTF-8

Codecs encode python (модуль codecs.encode в python)

Проблема: несоответствие кодировки при чтении. Для предотвращения ошибок полезно определять кодировку файла перед чтением, например, с помощью chardet.

Как закодировать строку или байты в Base64 и обратно?

Base64 часто используется для передачи двоичных данных через текстовые протоколы (например, в JSON или URL). Для работы с Base64 применяется модуль base64.


import base64

# Кодирование строки в Base64 (предварительно преобразуем в bytes)
text = "Python кодирование"
encoded = base64.b64encode(text.encode('utf-8'))
print(encoded)  # b'UHl0aG9uINC60L7QtNC40YDQvtCy0LDQvdC40LU='

# Декодирование
decoded_bytes = base64.b64decode(encoded)
decoded_text = decoded_bytes.decode('utf-8')
print(decoded_text)  # Python кодирование

Python encoding cp1251 (кодировка cp1251 в python)

Ошибка: использование неверной кодировки при преобразовании строки в байты перед кодированием Base64. Важно помнить, что Base64 работает с байтами, поэтому строку нужно сначала закодировать в bytes.

Как закодировать строку для использования в URL?

URL может содержать только определенные символы. Для преобразования специальных символов (пробелы, кириллица, знаки) в процентно-кодированный вид применяется функция urllib.parse.quote.


from urllib.parse import quote, unquote

query = "Привет, мир!"
encoded_query = quote(query)
print(encoded_query)  # %D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82%2C%20%D0%BC%D0%B8%D1%80!

decoded_query = unquote(encoded_query)
print(decoded_query)  # Привет, мир!

Python encoding utf (кодировка utf в python)

Ошибка: кодирование уже закодированной строки может привести к двойному кодированию. Рекомендуется кодировать только исходные данные, не прошедшие обработку.

Как преобразовать строку в шестнадцатеричное представление и обратно?

Шестнадцатеричное представление удобно для отладки, передачи двоичных данных в читаемой форме. Используется модуль binascii.


import binascii

text = "Hex test"
# Кодируем строку в bytes, затем в hex
hex_str = binascii.hexlify(text.encode('utf-8'))
print(hex_str)  # b'4865782074657374'

# Декодируем обратно
orig_bytes = binascii.unhexlify(hex_str)
print(orig_bytes.decode('utf-8'))  # Hex test

Unicode символ python (unicode символ в python)

Проблема: hexlify возвращает bytes, а не строку. Для получения строки можно вызвать .decode('ascii').

Как узнать, в какой кодировке закодирован файл или строка?

Для автоматического определения кодировки используется сторонняя библиотека chardet. Она анализирует байты и выдает предполагаемую кодировку с уровнем уверенности.


import chardet

data = b'\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82'
result = chardet.detect(data)
print(result)  # {'encoding': 'utf-8', 'confidence': 0.99, ...}

# Декодируем с обнаруженной кодировкой
encoding = result['encoding']
print(data.decode(encoding))  # Привет

Python coding utf (кодировка utf-8 в python)

Ошибка: chardet может ошибаться, особенно на коротких данных. После определения полезно проверять результат декодирования на наличие недопустимых символов.

Как привести строку к стандартной форме нормализации Unicode?

Unicode допускает несколько представлений одного и того же символа (например, символ 'é' может быть одним кодом или комбинацией 'e' + диакритический знак). Нормализация (NFC, NFD, NFKC, NFKD) приводит строку к единой форме. Это важно для сравнения строк и поиска.


import unicodedata

s1 = '\u00e9'  # é в форме NFC
s2 = 'e\u0301'  # e + ударение в форме NFD

print(s1, s2)  # é é (визуально одинаково)
print(s1 == s2)  # False

# Приводим к NFC
normalized = unicodedata.normalize('NFC', s2)
print(normalized == s1)  # True

Проблема: некорректное сравнение строк, если не учитывать нормализацию. Рекомендуется нормализовать все вводимые данные перед обработкой.

Расширенные примеры кодирования и декодирования

Пример 1: Пакетная конвертация текстовых файлов из CP1251 в UTF-8

Допустим, есть несколько файлов в кодировке Windows-1251, которые требуется перекодировать в UTF-8 с сохранением исходной структуры папок. Используем os.walk и модуль codecs.

Пример

import os
import codecs

source_dir = './cp1251_files'
target_dir = './utf8_files'

for root, dirs, files in os.walk(source_dir):
    relative = os.path.relpath(root, source_dir)
    target_path = os.path.join(target_dir, relative)
    os.makedirs(target_path, exist_ok=True)
    
    for file in files:
        if file.endswith('.txt'):
            src = os.path.join(root, file)
            dst = os.path.join(target_path, file)
            
            # Чтение в кодировке cp1251
            with codecs.open(src, 'r', encoding='cp1251') as f:
                content = f.read()
            
            # Запись в utf-8
            with codecs.open(dst, 'w', encoding='utf-8') as f:
                f.write(content)

print('Конвертация завершена')

Результат: все .txt файлы из cp1251_files и подпапок скопированы в utf8_files с перекодировкой.

Конвертация завершена

Пример 2: Отправка HTTP POST запроса с телом в JSON, содержащим нелатинские символы

При работе с REST API важно правильно кодировать данные. JSON строка по умолчанию должна быть в UTF-8, но библиотека requests делает это автоматически, если передать словарь.

Пример

import requests
import json

url = 'https://httpbin.org/post'
data = {'name': 'Иван', 'city': 'Москва'}

# Передача словаря - requests сам кодирует в JSON и устанавливает заголовки
response = requests.post(url, json=data)
print(response.status_code)
print(response.json()['json'])  # Покажет переданный JSON

# Если нужно передать вручную:
payload = json.dumps(data, ensure_ascii=False).encode('utf-8')
headers = {'Content-Type': 'application/json; charset=utf-8'}
response2 = requests.post(url, data=payload, headers=headers)
print(response2.json()['data'])

Результат: сервер получает данные с правильной кодировкой.

200
{'name': 'Иван', 'city': 'Москва'}
{
  "name": "Иван",
  "city": "Москва"
}

Пример 3: Определение кодировки и декодирование данных, полученных из сети

При скачивании веб-страниц или ответов API кодировка может быть указана в заголовке Content-Type или мета-теге, но иногда она не указана. Используем chardet для автоматического определения.

Пример

import requests
import chardet

url = 'https://example.com'
response = requests.get(url)

# Получаем байты ответа
content_bytes = response.content

# Определяем кодировку
detected = chardet.detect(content_bytes)
encoding = detected['encoding']
confidence = detected['confidence']
print(f"Определенная кодировка: {encoding} (уверенность: {confidence:.2%})")

# Декодируем с найденной кодировкой
text = content_bytes.decode(encoding, errors='replace')
print(text[:200])  # Первые 200 символов

# Альтернатива: используем response.encoding, если кодировка была в заголовке
if response.encoding and response.encoding.lower() != encoding.lower():
    print(f"Заголовок указывает {response.encoding}, но chardet определил {encoding}")

Результат: корректное декодирование содержимого страницы.

Определенная кодировка: utf-8 (уверенность: 99.00%)
<!doctype html>
<html>
<head>
    <title>Example Domain</title>
...

Кодирование и декодирование в Python - comments

En
Encoding decoding python (python)