Базовый строковый тип str как байтовая последовательность
Тип str в Python 2: байтовая строка по умолчанию
Основной подход: явное разделение str и unicode
В Python 2 строковый тип str представляет собой последовательность байтов. Для символов, не входящих в ASCII, требуется работа с типом unicode. Наиболее эффективное решение - всегда использовать unicode внутри программы и преобразовывать в байтовую строку только при вводе/выводе.
Пример объявления unicode-строки:
# Литерал Unicode в Python 2
my_string = u"Привет, мир!"
print type(my_string) # <type 'unicode'>Str python 2 (тип str в python 2)
Преобразование в байтовую строку (str) через кодировку UTF-8:
byte_str = my_string.encode('utf-8')
print type(byte_str) # <type 'str'>
print repr(byte_str) # '\xd0\x9f\xd1\x80...'Str python 3 (тип str в python 3)
Обратное преобразование:
decoded_back = byte_str.decode('utf-8')
print type(decoded_back) # <type 'unicode'>Как избежать путаницы между str и unicode в исходном коде?
Решение: использовать from __future__ import unicode_literals. Тогда все строковые литералы без префикса становятся unicode, а не str.
from __future__ import unicode_literals
normal = "строка" # это unicode, а не str
print type(normal) # <type 'unicode'>Проблема:
Если в коде есть конструкции, полагающиеся на str (например,%s форматирование), может возникнуть неявное преобразование. Рекомендуется всегда явно указывать .encode() при выводе.Как обрабатывать файлы с разными кодировками в Python 2?
Решение: использовать io.open с указанием кодировки, чтобы сразу получать unicode.
import io
with io.open('text.txt', 'w', encoding='utf-8') as f:
f.write(u'Текст на русском')
with io.open('text.txt', 'r', encoding='utf-8') as f:
content = f.read()
print type(content) # <type 'unicode'>Типичная ошибка:
Использование встроеннойopen() без кодировки возвращает str (байты). Попытка конкатенации str и unicode вызывает UnicodeDecodeError. Решение: всегда явно преобразовывать типы.Как правильно склеивать str и unicode?
Решение: преобразовать обе части к одному типу (обычно к unicode).
byte_part = 'hello '
unicode_part = u'мир'
# Неправильно: result = byte_part + unicode_part # UnicodeDecodeError
# Правильно:
result = byte_part.decode('ascii') + unicode_part
print result # hello мирВажно:
Python 2 автоматически пытается преобразовать str к unicode при конкатенации, используя ASCII. Если строка содержит не-ASCII байты, возникает ошибка.Как обработать UnicodeEncodeError при выводе в консоль?
Решение: настроить локаль или явно кодировать вывод.
my_unicode = u'Привет'
# print my_unicode # может вызвать UnicodeEncodeError
# Безопасный вывод:
print my_unicode.encode('utf-8')Проблема:
Консоль часто использует кодировку CP1251 или UTF-8. Явное указание кодировки решает проблему.Расширенные примеры работы со str в Python 2
Пример 1. Преобразование str в unicode и обратно с разными кодировками
byte_string = '\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82' # байты 'Привет' в utf-8
print type(byte_string) # <type 'str'>
unicode_string = byte_string.decode('utf-8')
print unicode_string # Привет
# Кодирование в другую кодировку (CP1251)
cp1251_bytes = unicode_string.encode('cp1251')
print repr(cp1251_bytes) # '\xcf\xf0\xe8\xe2\xe5\xf2'<type 'str'> Привет '\xcf\xf0\xe8\xe2\xe5\xf2'
Пример 2. Использование from __future__ import unicode_literals
from __future__ import unicode_literals
# Все литералы - unicode
s1 = "Текст"
s2 = u"Текст"
print type(s1) == type(s2) # True (оба unicode)
# Но байтовую строку можно создать через b''
byte_s = b'байтовый'
print type(byte_s) # <type 'str'>True <type 'str'>
Пример 3. Работа с кириллицей в именах файлов
import os
# Создание файла с кириллическим именем
filename = u'файл.txt'
with open(filename.encode('utf-8'), 'w') as f:
f.write('test')
# Чтение списка файлов
files = os.listdir(u'.')
for f in files:
print repr(f) # вывод в байтах
# если f - байтовая строка, декодировать
print f.decode('utf-8') if isinstance(f, str) else f'\xd1\x84\xd0\xb0\xd0\xb9\xd0\xbb.txt' файл.txt
Пример 4. Обработка ошибок с помощью errors параметра
# Строка с символами, не представимыми в Latin-1
unicode_str = u'Привет résumé'
# Попытка кодирования с игнорированием ошибок
latin_ignored = unicode_str.encode('latin-1', errors='ignore')
print repr(latin_ignored) # '?\xef? (только ASCII символы остались)
# Замена на знак вопроса
latin_replace = unicode_str.encode('latin-1', errors='replace')
print repr(latin_replace) # '??????????? r?sum?''?\xef?' '??????????? r?sum?'
Пример 5. Сравнение str и unicode
# Две одинаковые строки в разных типах
byte_str = 'abc'
uni_str = u'abc'
print byte_str == uni_str # True (Python 2 сравнивает по содержанию)
# Но с не-ASCII byte_str != uni_str, если кодировка не совпадает
byte_utf = '\xc3\xa9' # é в UTF-8
uni = u'\xe9' # é в юникоде
print byte_utf == uni # True (если байты корректно декодируются)True True