Python 2: управление текстовыми кодировками

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

Основы кодировок в Python 2

Python 2 имеет две основные строковые структуры: str (байтовая строка) и unicode (строка в кодировке Unicode). Работа с кодировками часто вызывает трудности из-за неявных преобразований и различий в поведении. Ниже рассматриваются ключевые подходы и типичные сценарии.

Основное рекомендуемое решение – всегда работать с Unicode внутри программы, а на границах (ввод/вывод, чтение файлов, сетевые операции) явно выполнять кодирование и декодирование. Для этого следует:

  • Указывать кодировку исходного файла (в первой или второй строке): # -*- coding: utf-8 -*-.
  • Использовать from __future__ import unicode_literals – тогда все строковые литералы становятся Unicode.
  • Для чтения/записи файлов применять io.open с явным указанием кодировки.
# Файл с явным указанием кодировки
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import io

with io.open('file.txt', 'w', encoding='utf-8') as f:
    f.write('Привет, мир!')

with io.open('file.txt', 'r', encoding='utf-8') as f:
    data = f.read()
    print(type(data))  # <class 'unicode'>

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

Этот подход предотвращает большую часть ошибок, связанных с кодировками.

Как работать с файлами в разных кодировках?

Если необходимо прочитать или записать файл в конкретной кодировке (например, cp1251, latin-1), удобно использовать модуль codecs или io.open.

import codecs

# Запись в cp1251
with codecs.open('file_cp1251.txt', 'w', encoding='cp1251') as f:
    f.write(u'Привет')

# Чтение из cp1251
with codecs.open('file_cp1251.txt', 'r', encoding='cp1251') as f:
    data = f.read()
    print(type(data))  # unicode

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

Типичная ошибка: при попытке прочитать файл в кодировке, отличной от той, в которой он сохранён, возникает UnicodeDecodeError. Решение – точно узнать кодировку файла (например, с помощью утилиты chardet) и указать её при открытии.

Как избежать UnicodeEncodeError при выводе в консоль?

В Windows консоль часто использует cp866 или cp1251, в Linux – UTF-8. Ошибка возникает, когда байтовая строка содержит символы, не представимые в кодировке консоли. Решение – явно кодировать строку в подходящую кодировку.

s = u'Привет'
print s.encode('utf-8')  # только для консоли, поддерживающей UTF-8
# или для Windows:
# print s.encode('cp866')

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

Проблема: если кодировка консоли не совпадает с указанной при выводе, результат будет неправильным. Решение: установить кодировку консоли программно (например, sys.setdefaultencoding – но это опасно) или использовать сторонние библиотеки (например, win-unicode-console).

Как определить кодировку байтовой строки?

Однозначно определить кодировку невозможно, но можно сделать предположение с помощью сторонних библиотек, например chardet.

import chardet

data = '\xff\xfe\x41\x00'  # пример BOM для UTF-16
result = chardet.detect(data)
print(result['encoding'])  # 'UTF-16'

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

Ограничение: chardet не гарантирует 100% точность, особенно для коротких строк. Совет: по возможности полагаться на мета-информацию (HTTP заголовки, Content-Type).

Как перевести байтовую строку в Unicode, если кодировка известна?

Используется метод .decode() для str и .encode() для unicode.

byte_str = '\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82'  # UTF-8
unicode_str = byte_str.decode('utf-8')
print unicode_str  # Привет

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

Ошибка: если в строке присутствуют байты, недопустимые в указанной кодировке, вызов decode приведёт к UnicodeDecodeError. Решение: использовать обработчик ошибок, например 'replace' или 'ignore'.

byte_str = '\xff\xfe\x41'
unicode_str = byte_str.decode('utf-8', errors='replace')
print repr(unicode_str)  # u'\ufffd\ufffdA' (заменяет недопустимые байты на �)

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

Как использовать from __future__ import unicode_literals для упрощения кода?

Эта директива делает все строковые литералы Unicode (кроме байтовых литералов с префиксом b).

from __future__ import unicode_literals

s = 'эта строка unicode'
print type(s)  # <class 'unicode'>

b = b'эта строка bytes'
print type(b)  # <class 'str'>

Внимание: при использовании этой директивы код, написанный для Python 2, может сломаться, если он полагается на байтовую природу строк (например, при работе с бинарными протоколами). Решение: явно использовать b'' для байтовых литералов в таких местах.

- Unicode символ python (unicode символ в python)
- Python coding utf (кодировка utf-8 в python)

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

Пример 1: Чтение файла в UTF-8 и запись в cp1251 с заменой неподдерживаемых символов

Пример
# -*- coding: utf-8 -*-
import io

# Чтение UTF-8
with io.open('source_utf8.txt', 'r', encoding='utf-8') as f:
    text = f.read()

# Запись в cp1251 с игнорированием символов, отсутствующих в cp1251
with io.open('output_cp1251.txt', 'w', encoding='cp1251', errors='ignore') as f:
    f.write(text)

# Результат: символы вроде '№' будут потеряны

Пояснение: обработчик errors='ignore' просто пропускает символы, которые нельзя представить в указанной кодировке. Альтернатива – 'replace' (заменяет на '?') или 'xmlcharrefreplace' (превращает в &#NNN;).

Пример 2: Работа с HTTP-ответами, содержащими разные кодировки

Пример
import urllib2

# Запрос к странице, возвращающей данные в UTF-8
response = urllib2.urlopen('http://example.com')
html_bytes = response.read()
# Попытка декодировать в UTF-8
html_unicode = html_bytes.decode('utf-8', errors='replace')
# Альтернатива: получить кодировку из заголовка Content-Type
content_type = response.headers.get('Content-Type', '')
if 'charset=' in content_type:
    cs = content_type.split('charset=')[1].split(';')[0].strip()
    html_unicode2 = html_bytes.decode(cs, errors='replace')
# Результат: html_unicode – строка Unicode, пригодная для дальнейшей обработки.

Пример 3: Использование chardet для автоматического определения кодировки файла

Пример
import chardet

def read_file_with_auto_encoding(filename):
    with open(filename, 'rb') as f:
        raw = f.read()
        detected = chardet.detect(raw)
        encoding = detected['encoding']
        print('Detected encoding:', encoding)
        return raw.decode(encoding)

text = read_file_with_auto_encoding('sample.txt')

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

Пример 4: Явное указание кодировки при использовании raw_input в консоли Windows

Пример
# -*- coding: utf-8 -*-
import sys

# Установка кодировки консоли (не рекомендуется, но для примера)
reload(sys)
sys.setdefaultencoding('utf-8')

# Ввод от пользователя – всегда байтовая строка
user_input = raw_input('Введите текст: ')
# Декодируем в Unicode вручную (предполагаем кодировку консоли)
user_unicode = user_input.decode(sys.stdin.encoding or 'utf-8')
print 'Вы ввели:', user_unicode.encode('utf-8')

Предупреждение: sys.setdefaultencoding может нарушить работу некоторых библиотек и обычно не рекомендуется. Лучше использовать явное декодирование с известной кодировкой консоли.

Пример 5: Объединение байтовых и Unicode-строк – неявное преобразование

Пример
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

bytes_str = 'привет'.encode('utf-8')  # байтовая строка
unicode_str = u' мир'

# В Python 2 это приведёт к ошибке UnicodeDecodeError при попытке неявного преобразования
# result = bytes_str + unicode_str  # Ошибка: 'utf-8' codec can't decode byte ...

# Правильно – явно декодировать байтовую строку
result = bytes_str.decode('utf-8') + unicode_str
print result  # привет мир
# Вывод: привет мир (Unicode)

Пример 6: Обработка BOM (Byte Order Mark) в файлах UTF-8/UTF-16

Пример
import codecs

# Чтение файла с BOM (например, UTF-8 with BOM)
with codecs.open('file_with_bom.txt', 'r', encoding='utf-8-sig') as f:
    data = f.read()
# utf-8-sig автоматически удаляет BOM (\ufeff) из начала
print repr(data[:10])

Пояснение: кодировка 'utf-8-sig' (или 'utf-16' с учётом порядка байт) упрощает работу с документами, содержащими BOM.

Кодировки в Python 2 - comments

En
Python 2 encoding (python)