Кодировки python: Кодировки в python

Содержание

Кодировки в python

2010-03-27 python кодировки

прочтение статьи занимает 4мин, с обдумыванием — 10мин 🙂
примечание: данная статья относится к python 2.x, но для python 3.x принцип тот же

Общее

В python есть 2 объекта работающими с текстом: unicode и str, объект unicode хранит символы в формате (кодировке) unicode, объект str является набором байт/символов в которых python хранит остальные кодировки (utf8, cp1251, cp866, koi8-r и др).

Кодировку unicode можно считать рабочей кодировкой питона т.к. она предназначена для её использования в самом скрипте — для разных операций над строками.
Внешняя кодировка (объект str) предназначена для хранения и передачи текстовой информации вне скрипта, например для сохранения в файл или передачи по сети. Поэтому в данной статье я её назвал внешней. Самой используемой кодировкой в мире является utf8 и число приложений переходящих на эту кодировку растет каждый день, таким образом превращаясь в «стандарт». Эта кодировка хороша тем что для хранения текста она занимает оптимальное кол-во памяти и с помощью её можно закодировать почти все языки мира ( в отличие от cp1251 и подобных однобайтовых кодировок).

Поэтому рекомендуется везде использовать utf8, и при написании скриптов.

Использование

Скрипт питона, в самом начале скрипта указываем кодировку файла и сохраняем в ней файл
либодля того что-бы интерпретатор python понял в какой кодировке файл

Строки в скрипте
Строки в скрипте хранятся байтами, от кавычки до кавычки:

= 6 байт при cp1251
= 12 байт при utf8

Если перед строкой добавить символ u, то при запуске скрипта, эта байтовая строка будет декодирована в unicode из кодировки указанной в начале:

# coding:utf8
print u'Привет'
и если кодировка содержимого в файле отличается от указанной, то в строке могут быть «битые символы»

Загрузка и сохранение файла

# coding: utf8 # Загружаем файл с кодировкай utf8 text = open('file.txt','r').read() # Декодируем из utf8 в unicode - из внешней в рабочую text = text.decode('utf8') # Работаем с текстом text += text # Кодируем тест из unicode в utf8 - из рабочей во внешнюю text = text.encode('utf8') # Сохраняем в файл с кодировкий utf8 open('file.txt','w').write(text)

Текст в скрипте

# coding: utf8

a = 'Текст в utf8'
b = u'Текст в unicode'
# Эквивалентно: b = 'Текст в unicode'.decode('utf8')
# т.к. сам скрипт хранится в utf8

print 'a =',type(a),a
# декодируем из utf-8 в unicode и далее unicode в cp866 (кодировка консоли winXP ru)
print 'a2 =',type(a),a.decode('utf8').encode('cp866')
print 'b =',type(b),b
Процедуре print текст желательно передавать в рабочей кодировке либо кодировать в кодировку ОС.
Результат скрипта при запуске из консоли windows XP:
a = <type 'str'> ╨в╨╡╨║╤Б╤В ╨▓ utf8
a2 = <type 'str'> Текст в utf8
b = <type 'unicode'> Текст в unicode
В последней строке print преобразовал unicode в cp866 автоматический, см. следующий пункт

Авто-преобразование кодировки
В некоторых случаях для упрощения разработки python делает преобразование кодировки, пример с методом print можно посмотреть в предыдущем пункте.
В примере ниже, python сам переводит utf8 в unicode — приводит к одной кодировке для того что-бы сложить строки.

# coding: utf8

# Устанавливаем стандартную внешнюю кодировку = utf8
import sys
reload(sys)
sys.setdefaultencoding('utf8')

a = 'Текст в utf8'
b = u'Текст в unicode'
c = a + b

print 'a =',type(a),a
print 'b =',type(b),b
print 'c =',type(c),c
Результат
a = <type 'str'> Текст в utf8
b = <type 'unicode'> Текст в unicode
c = <type 'unicode'> Текст в utf8Текст в unicode
Как видим результирующая строка «c» в unicode. Если бы кодировки строк совпадали то авто-перекодирования не произошло бы и результирующая строка содержала кодировку слагаемых строк.
Авто-перекодирование обычно срабатывает когда происходит взаимодействие разных кодировок.

Пример авто-преобразования кодировок в сравнении

# coding: utf8

# Устанавливаем стандартную внешнюю кодировку = utf8
import sys
reload(sys)
sys.setdefaultencoding('utf8')

print '1. utf8 and unicode', 'true' if u'Слово'.encode('utf8') == u'Слово' else 'false'
print '2. utf8 and cp1251', 'true' if u'Слово'.encode('utf8') == u'Слово'.encode('cp1251') else 'false'
print '3. cp1251 and unicode', 'true' if u'Слово'.encode('cp1251') == u'Слово' else 'false'

Результат
1. utf8 and unicode true
2. utf8 and cp1251 false
script.py:10: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
  print '3. cp1251 and unicode', 'true' if u'Слово'.encode('cp1251') == u'Слово' else 'false'
3. cp1251 and unicode false
В сравнении 1, кодировка utf8 преобразовалась в unicode и сравнение произошло корректно.
В сравнении 2, сравниваются кодировки одного вида — обе внешние, т.к. кодированы они в разных кодировках условие выдало что они не равны.
В сравнении 3, выпало предупреждение из за того что выполняется сравнение кодировок разного вида — рабочая и внешняя, а авто-декодирование не произошло т.к. стандартная внешняя кодировка = utf8, и декодировать строку в кодировке cp1251 методом utf8 питон не смог.

Вывод списков

# coding: utf8

d = ['Тест','списка']

print '1',d
print '2',d.__repr__()
print '3',

Проблемы с кодировкой в Python

В python есть 2 объекта работающими с текстом: unicode и str, объект unicode хранит символы в формате (кодировке) unicode, объект str является набором байт/символов в которых python хранит остальные кодировки (utf8, cp1251, cp866, koi8-r и др).

Кодировку unicode можно считать рабочей кодировкой питона т.к. она предназначена для её использования в самом скрипте — для разных операций над строками.

Внешняя кодировка (объект str) предназначена для хранения и передачи текстовой информации вне скрипта, например для сохранения в файл или передачи по сети. Поэтому в данной статье я её назвал внешней. Самой используемой кодировкой в мире является utf8 и число приложений переходящих на эту кодировку растет каждый день, таким образом превращаясь в «стандарт».

Эта кодировка хороша тем что для хранения текста она занимает оптимальное кол-во памяти и с помощью её можно закодировать почти все языки мира ( в отличие от cp1251 и подобных однобайтовых кодировок). Поэтому рекомендуется везде использовать utf8, и при написании скриптов.

Использование

Скрипт питона, в самом начале скрипта указываем кодировку файла и сохраняем в ней файл

# coding: utf8

либо

# -*- coding: utf-8 -*-

для того что-бы интерпретатор python понял в какой кодировке файл

Строки в скрипте

Строки в скрипте хранятся байтами, от кавычки до кавычки:

print 'Привет'

= 6 байт при cp1251

= 12 байт при utf8

Если перед строкой добавить символ u, то при запуске скрипта, эта байтовая строка будет декодирована в unicode из кодировки указанной в начале:

# coding:utf8
print u'Привет'

и если кодировка содержимого в файле отличается от указанной, то в строке могут быть «битые символы»

Загрузка и сохранение файла

# coding: utf8
# Загружаем файл с кодировкай utf8
text = open('file.txt','r').read()
# Декодируем из utf8 в unicode - из внешней в рабочую
text = text.decode('utf8')
# Работаем с текстом
text += text
# Кодируем тест из unicode в utf8 - из рабочей во внешнюю
text = text.encode('utf8')
# Сохраняем в файл с кодировкий utf8
open('file.txt','w').write(text)

Текст в скрипте

# coding: utf8
a = 'Текст в utf8'
b = u'Текст в unicode'
# Эквивалентно: b = 'Текст в unicode'.decode('utf8')
# т.к. сам скрипт хранится в utf8
print 'a =',type(a),a
# декодируем из utf-8 в unicode и далее unicode в cp866 (кодировка консоли winXP ru)
print 'a2 =',type(a),a.decode('utf8').encode('cp866')
print 'b =',type(b),b

Процедуре print текст желательно передавать в рабочей кодировке либо кодировать в кодировку ОС.

Результат скрипта при запуске из консоли windows XP:

a = ╨в╨╡╨║╤Б╤В ╨▓ utf8

a2 = Текст в utf8

b = Текст в unicode

В последней строке print преобразовал unicode в cp866 автоматический, см. следующий пункт

Авто-преобразование кодировки

В некоторых случаях для упрощения разработки python делает преобразование кодировки, пример с методом print можно посмотреть в предыдущем пункте.

В примере ниже, python сам переводит utf8 в unicode — приводит к одной кодировке для того что-бы сложить строки.

# coding: utf8
# Устанавливаем стандартную внешнюю кодировку = utf8
import sys
reload(sys)
sys.setdefaultencoding('utf8')
a = 'Текст в utf8'
b = u'Текст в unicode'
c = a + b
print 'a =',type(a),a
print 'b =',type(b),b
print 'c =',type(c),c

Результат

a = Текст в utf8

b = Текст в unicode

c = Текст в utf8Текст в unicode

Как видим результирующая строка «c» в unicode. Если бы кодировки строк совпадали то авто-перекодирования не произошло бы и результирующая строка содержала кодировку слагаемых строк.

Авто-перекодирование обычно срабатывает когда происходит взаимодействие разных кодировок.

Пример авто-преобразования кодировок в сравнении

# coding: utf8
# Устанавливаем стандартную внешнюю кодировку = utf8
import sys
reload(sys)
sys.setdefaultencoding('utf8')
print '1. utf8 and unicode', 'true' if u'Слово'.encode('utf8') == u'Слово' else 'false'
print '2. utf8 and cp1251', 'true' if u'Слово'.encode('utf8') == u'Слово'.encode('cp1251') else 'false'
print '3. cp1251 and unicode', 'true' if u'Слово'.encode('cp1251') == u'Слово' else 'false'

Результат

1. utf8 and unicode true

2. utf8 and cp1251 false

script.py:10: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode — interpreting them as being unequal

print ‘3. cp1251 and unicode’, ‘true’ if u’Слово’.encode(‘cp1251′) == u’Слово’ else ‘false’

3. cp1251 and unicode false

В сравнении 1, кодировка utf8 преобразовалась в unicode и сравнение произошло корректно.

В сравнении 2, сравниваются кодировки одного вида — обе внешние, т.к. кодированы они в разных кодировках условие выдало что они не равны.

В сравнении 3, выпало предупреждение из за того что выполняется сравнение кодировок разного вида — рабочая и внешняя, а авто-декодирование не произошло т.к. стандартная внешняя кодировка = utf8, и декодировать строку в кодировке cp1251 методом utf8 питон не смог.

Вывод списков

# coding: utf8
d = ['Тест','списка']
print '1',d
print '2',d.__repr__()
print '3',','.join(d)

Результат:

1 [‘\xd0\xa2\xd0\xb5\xd1\x81\xd1\x82’, ‘\xd1\x81\xd0\xbf\xd0\xb8\xd1\x81\xd0\xba\xd0\xb0’]

2 [‘\xd0\xa2\xd0\xb5\xd1\x81\xd1\x82’, ‘\xd1\x81\xd0\xbf\xd0\xb8\xd1\x81\xd0\xba\xd0\xb0’]

3 Тест,списка

При выводе списка, происходит вызов [{repr}]() который возвращает внутреннее представление этого спиcка — print 1 и 2 являются аналогичными. Для корректного вывода списка, его нужно преобразовать в строку — print 3.

Установка внешней кодировки при запуске

PYTHONIOENCODING=utf8 python 1.py

В обучении ребенка важно правильное толкование окружающего его мира. Существует масса полезных журналов которые начнут экологическое воспитание дошкольников правильным путем. Развивать интерес к окружающему миру очень трудный но интересный процесс, уделите этому особое внимание.

Получить список всех кодировок, которые Python может кодировать в

другие ответы здесь, похоже, указывают на то, что построение этого списка программно трудно и чревато ловушками. Однако это, вероятно, не нужно, так как документация содержит полный список стандартных кодировок, поддерживаемых Python, и сделала это с Python 2.3.

вы можете найти эти списки (для каждой стабильной версии языка, выпущенных до сих пор) являются списками для каждой документированной версии Python. Обратите внимание, что если вы хотите обратную совместимость, а не просто поддержку определенной версии Python, вы можете просто скопировать список из последний версия Python и проверьте, существует ли каждая кодировка в Python под управлением вашей программы прежде чем пытаться использовать его.

в Python 2.3 (59 кодировки)

['ascii',
 'cp037',
 'cp424',
 'cp437',
 'cp500',
 'cp737',
 'cp775',
 'cp850',
 'cp852',
 'cp855',
 'cp856',
 'cp857',
 'cp860',
 'cp861',
 'cp862',
 'cp863',
 'cp864',
 'cp865',
 'cp869',
 'cp874',
 'cp875',
 'cp1006',
 'cp1026',
 'cp1140',
 'cp1250',
 'cp1251',
 'cp1252',
 'cp1253',
 'cp1254',
 'cp1255',
 'cp1256',
 'cp1257',
 'cp1258',
 'latin_1',
 'iso8859_2',
 'iso8859_3',
 'iso8859_4',
 'iso8859_5',
 'iso8859_6',
 'iso8859_7',
 'iso8859_8',
 'iso8859_9',
 'iso8859_10',
 'iso8859_13',
 'iso8859_14',
 'iso8859_15',
 'koi8_r',
 'koi8_u',
 'mac_cyrillic',
 'mac_greek',
 'mac_iceland',
 'mac_latin2',
 'mac_roman',
 'mac_turkish',
 'utf_16',
 'utf_16_be',
 'utf_16_le',
 'utf_7',
 'utf_8']

в Python 2.4 (85 кодировки)

['ascii',
 'big5',
 'big5hkscs',
 'cp037',
 'cp424',
 'cp437',
 'cp500',
 'cp737',
 'cp775',
 'cp850',
 'cp852',
 'cp855',
 'cp856',
 'cp857',
 'cp860',
 'cp861',
 'cp862',
 'cp863',
 'cp864',
 'cp865',
 'cp866',
 'cp869',
 'cp874',
 'cp875',
 'cp932',
 'cp949',
 'cp950',
 'cp1006',
 'cp1026',
 'cp1140',
 'cp1250',
 'cp1251',
 'cp1252',
 'cp1253',
 'cp1254',
 'cp1255',
 'cp1256',
 'cp1257',
 'cp1258',
 'euc_jp',
 'euc_jis_2004',
 'euc_jisx0213',
 'euc_kr',
 'gb2312',
 'gbk',
 'gb18030',
 'hz',
 'iso2022_jp',
 'iso2022_jp_1',
 'iso2022_jp_2',
 'iso2022_jp_2004',
 'iso2022_jp_3',
 'iso2022_jp_ext',
 'iso2022_kr',
 'latin_1',
 'iso8859_2',
 'iso8859_3',
 'iso8859_4',
 'iso8859_5',
 'iso8859_6',
 'iso8859_7',
 'iso8859_8',
 'iso8859_9',
 'iso8859_10',
 'iso8859_13',
 'iso8859_14',
 'iso8859_15',
 'johab',
 'koi8_r',
 'koi8_u',
 'mac_cyrillic',
 'mac_greek',
 'mac_iceland',
 'mac_latin2',
 'mac_roman',
 'mac_turkish',
 'ptcp154',
 'shift_jis',
 'shift_jis_2004',
 'shift_jisx0213',
 'utf_16',
 'utf_16_be',
 'utf_16_le',
 'utf_7',
 'utf_8']

Python 2.5 (86 кодировки)

['ascii',
 'big5',
 'big5hkscs',
 'cp037',
 'cp424',
 'cp437',
 'cp500',
 'cp737',
 'cp775',
 'cp850',
 'cp852',
 'cp855',
 'cp856',
 'cp857',
 'cp860',
 'cp861',
 'cp862',
 'cp863',
 'cp864',
 'cp865',
 'cp866',
 'cp869',
 'cp874',
 'cp875',
 'cp932',
 'cp949',
 'cp950',
 'cp1006',
 'cp1026',
 'cp1140',
 'cp1250',
 'cp1251',
 'cp1252',
 'cp1253',
 'cp1254',
 'cp1255',
 'cp1256',
 'cp1257',
 'cp1258',
 'euc_jp',
 'euc_jis_2004',
 'euc_jisx0213',
 'euc_kr',
 'gb2312',
 'gbk',
 'gb18030',
 'hz',
 'iso2022_jp',
 'iso2022_jp_1',
 'iso2022_jp_2',
 'iso2022_jp_2004',
 'iso2022_jp_3',
 'iso2022_jp_ext',
 'iso2022_kr',
 'latin_1',
 'iso8859_2',
 'iso8859_3',
 'iso8859_4',
 'iso8859_5',
 'iso8859_6',
 'iso8859_7',
 'iso8859_8',
 'iso8859_9',
 'iso8859_10',
 'iso8859_13',
 'iso8859_14',
 'iso8859_15',
 'johab',
 'koi8_r',
 'koi8_u',
 'mac_cyrillic',
 'mac_greek',
 'mac_iceland',
 'mac_latin2',
 'mac_roman',
 'mac_turkish',
 'ptcp154',
 'shift_jis',
 'shift_jis_2004',
 'shift_jisx0213',
 'utf_16',
 'utf_16_be',
 'utf_16_le',
 'utf_7',
 'utf_8',
 'utf_8_sig']

в Python 2.6 (90 кодировки)

['ascii',
 'big5',
 'big5hkscs',
 'cp037',
 'cp424',
 'cp437',
 'cp500',
 'cp737',
 'cp775',
 'cp850',
 'cp852',
 'cp855',
 'cp856',
 'cp857',
 'cp860',
 'cp861',
 'cp862',
 'cp863',
 'cp864',
 'cp865',
 'cp866',
 'cp869',
 'cp874',
 'cp875',
 'cp932',
 'cp949',
 'cp950',
 'cp1006',
 'cp1026',
 'cp1140',
 'cp1250',
 'cp1251',
 'cp1252',
 'cp1253',
 'cp1254',
 'cp1255',
 'cp1256',
 'cp1257',
 'cp1258',
 'euc_jp',
 'euc_jis_2004',
 'euc_jisx0213',
 'euc_kr',
 'gb2312',
 'gbk',
 'gb18030',
 'hz',
 'iso2022_jp',
 'iso2022_jp_1',
 'iso2022_jp_2',
 'iso2022_jp_2004',
 'iso2022_jp_3',
 'iso2022_jp_ext',
 'iso2022_kr',
 'latin_1',
 'iso8859_2',
 'iso8859_3',
 'iso8859_4',
 'iso8859_5',
 'iso8859_6',
 'iso8859_7',
 'iso8859_8',
 'iso8859_9',
 'iso8859_10',
 'iso8859_13',
 'iso8859_14',
 'iso8859_15',
 'iso8859_16',
 'johab',
 'koi8_r',
 'koi8_u',
 'mac_cyrillic',
 'mac_greek',
 'mac_iceland',
 'mac_latin2',
 'mac_roman',
 'mac_turkish',
 'ptcp154',
 'shift_jis',
 'shift_jis_2004',
 'shift_jisx0213',
 'utf_32',
 'utf_32_be',
 'utf_32_le',
 'utf_16',
 'utf_16_be',
 'utf_16_le',
 'utf_7',
 'utf_8',
 'utf_8_sig']

Python 2.7 (93 кодировки)

['ascii',
 'big5',
 'big5hkscs',
 'cp037',
 'cp424',
 'cp437',
 'cp500',
 'cp720',
 'cp737',
 'cp775',
 'cp850',
 'cp852',
 'cp855',
 'cp856',
 'cp857',
 'cp858',
 'cp860',
 'cp861',
 'cp862',
 'cp863',
 'cp864',
 'cp865',
 'cp866',
 'cp869',
 'cp874',
 'cp875',
 'cp932',
 'cp949',
 'cp950',
 'cp1006',
 'cp1026',
 'cp1140',
 'cp1250',
 'cp1251',
 'cp1252',
 'cp1253',
 'cp1254',
 'cp1255',
 'cp1256',
 'cp1257',
 'cp1258',
 'euc_jp',
 'euc_jis_2004',
 'euc_jisx0213',
 'euc_kr',
 'gb2312',
 'gbk',
 'gb18030',
 'hz',
 'iso2022_jp',
 'iso2022_jp_1',
 'iso2022_jp_2',
 'iso2022_jp_2004',
 'iso2022_jp_3',
 'iso2022_jp_ext',
 'iso2022_kr',
 'latin_1',
 'iso8859_2',
 'iso8859_3',
 'iso8859_4',
 'iso8859_5',
 'iso8859_6',
 'iso8859_7',
 'iso8859_8',
 'iso8859_9',
 'iso8859_10',
 'iso8859_11',
 'iso8859_13',
 'iso8859_14',
 'iso8859_15',
 'iso8859_16',
 'johab',
 'koi8_r',
 'koi8_u',
 'mac_cyrillic',
 'mac_greek',
 'mac_iceland',
 'mac_latin2',
 'mac_roman',
 'mac_turkish',
 'ptcp154',
 'shift_jis',
 'shift_jis_2004',
 'shift_jisx0213',
 'utf_32',
 'utf_32_be',
 'utf_32_le',
 'utf_16',
 'utf_16_be',
 'utf_16_le',
 'utf_7',
 'utf_8',
 'utf_8_sig']

в Python 3.0 (89 кодировки)

['ascii',
 'big5',
 'big5hkscs',
 'cp037',
 'cp424',
 'cp437',
 'cp500',
 'cp737',
 'cp775',
 'cp850',
 'cp852',
 'cp855',
 'cp856',
 'cp857',
 'cp860',
 'cp861',
 'cp862',
 'cp863',
 'cp864',
 'cp865',
 'cp866',
 'cp869',
 'cp874',
 'cp875',
 'cp932',
 'cp949',
 'cp950',
 'cp1006',
 'cp1026',
 'cp1140',
 'cp1250',
 'cp1251',
 'cp1252',
 'cp1253',
 'cp1254',
 'cp1255',
 'cp1256',
 'cp1257',
 'cp1258',
 'euc_jp',
 'euc_jis_2004',
 'euc_jisx0213',
 'euc_kr',
 'gb2312',
 'gbk',
 'gb18030',
 'hz',
 'iso2022_jp',
 'iso2022_jp_1',
 'iso2022_jp_2',
 'iso2022_jp_2004',
 'iso2022_jp_3',
 'iso2022_jp_ext',
 'iso2022_kr',
 'latin_1',
 'iso8859_2',
 'iso8859_3',
 'iso8859_4',
 'iso8859_5',
 'iso8859_6',
 'iso8859_7',
 'iso8859_8',
 'iso8859_9',
 'iso8859_10',
 'iso8859_13',
 'iso8859_14',
 'iso8859_15',
 'johab',
 'koi8_r',
 'koi8_u',
 'mac_cyrillic',
 'mac_greek',
 'mac_iceland',
 'mac_latin2',
 'mac_roman',
 'mac_turkish',
 'ptcp154',
 'shift_jis',
 'shift_jis_2004',
 'shift_jisx0213',
 'utf_32',
 'utf_32_be',
 'utf_32_le',
 'utf_16',
 'utf_16_be',
 'utf_16_le',
 'utf_7',
 'utf_8',
 'utf_8_sig']

в Python 3.1 (90 кодировки)

['ascii',
 'big5',
 'big5hkscs',
 'cp037',
 'cp424',
 'cp437',
 'cp500',
 'cp737',
 'cp775',
 'cp850',
 'cp852',
 'cp855',
 'cp856',
 'cp857',
 'cp860',
 'cp861',
 'cp862',
 'cp863',
 'cp864',
 'cp865',
 'cp866',
 'cp869',
 'cp874',
 'cp875',
 'cp932',
 'cp949',
 'cp950',
 'cp1006',
 'cp1026',
 'cp1140',
 'cp1250',
 'cp1251',
 'cp1252',
 'cp1253',
 'cp1254',
 'cp1255',
 'cp1256',
 'cp1257',
 'cp1258',
 'euc_jp',
 'euc_jis_2004',
 'euc_jisx0213',
 'euc_kr',
 'gb2312',
 'gbk',
 'gb18030',
 'hz',
 'iso2022_jp',
 'iso2022_jp_1',
 'iso2022_jp_2',
 'iso2022_jp_2004',
 'iso2022_jp_3',
 'iso2022_jp_ext',
 'iso2022_kr',
 'latin_1',
 'iso8859_2',
 'iso8859_3',
 'iso8859_4',
 'iso8859_5',
 'iso8859_6',
 'iso8859_7',
 'iso8859_8',
 'iso8859_9',
 'iso8859_10',
 'iso8859_13',
 'iso8859_14',
 'iso8859_15',
 'iso8859_16',
 'johab',
 'koi8_r',
 'koi8_u',
 'mac_cyrillic',
 'mac_greek',
 'mac_iceland',
 'mac_latin2',
 'mac_roman',
 'mac_turkish',
 'ptcp154',
 'shift_jis',
 'shift_jis_2004',
 'shift_jisx0213',
 'utf_32',
 'utf_32_be',
 'utf_32_le',
 'utf_16',
 'utf_16_be',
 'utf_16_le',
 'utf_7',
 'utf_8',
 'utf_8_sig']

в Python 3.2 (92 кодировки)

['ascii',
 'big5',
 'big5hkscs',
 'cp037',
 'cp424',
 'cp437',
 'cp500',
 'cp720',
 'cp737',
 'cp775',
 'cp850',
 'cp852',
 'cp855',
 'cp856',
 'cp857',
 'cp858',
 'cp860',
 'cp861',
 'cp862',
 'cp863',
 'cp864',
 'cp865',
 'cp866',
 'cp869',
 'cp874',
 'cp875',
 'cp932',
 'cp949',
 'cp950',
 'cp1006',
 'cp1026',
 'cp1140',
 'cp1250',
 'cp1251',
 'cp1252',
 'cp1253',
 'cp1254',
 'cp1255',
 'cp1256',
 'cp1257',
 'cp1258',
 'euc_jp',
 'euc_jis_2004',
 'euc_jisx0213',
 'euc_kr',
 'gb2312',
 'gbk',
 'gb18030',
 'hz',
 'iso2022_jp',
 'iso2022_jp_1',
 'iso2022_jp_2',
 'iso2022_jp_2004',
 'iso2022_jp_3',
 'iso2022_jp_ext',
 'iso2022_kr',
 'latin_1',
 'iso8859_2',
 'iso8859_3',
 'iso8859_4',
 'iso8859_5',
 'iso8859_6',
 'iso8859_7',
 'iso8859_8',
 'iso8859_9',
 'iso8859_10',
 'iso8859_13',
 'iso8859_14',
 'iso8859_15',
 'iso8859_16',
 'johab',
 'koi8_r',
 'koi8_u',
 'mac_cyrillic',
 'mac_greek',
 'mac_iceland',
 'mac_latin2',
 'mac_roman',
 'mac_turkish',
 'ptcp154',
 'shift_jis',
 'shift_jis_2004',
 'shift_jisx0213',
 'utf_32',
 'utf_32_be',
 'utf_32_le',
 'utf_16',
 'utf_16_be',
 'utf_16_le',
 'utf_7',
 'utf_8',
 'utf_8_sig']

Python 3.3 (93 кодировки)

['ascii',
 'big5',
 'big5hkscs',
 'cp037',
 'cp424',
 'cp437',
 'cp500',
 'cp720',
 'cp737',
 'cp775',
 'cp850',
 'cp852',
 'cp855',
 'cp856',
 'cp857',
 'cp858',
 'cp860',
 'cp861',
 'cp862',
 'cp863',
 'cp864',
 'cp865',
 'cp866',
 'cp869',
 'cp874',
 'cp875',
 'cp932',
 'cp949',
 'cp950',
 'cp1006',
 'cp1026',
 'cp1140',
 'cp1250',
 'cp1251',
 'cp1252',
 'cp1253',
 'cp1254',
 'cp1255',
 'cp1256',
 'cp1257',
 'cp1258',
 'cp65001',
 'euc_jp',
 'euc_jis_2004',
 'euc_jisx0213',
 'euc_kr',
 'gb2312',
 'gbk',
 'gb18030',
 'hz',
 'iso2022_jp',
 'iso2022_jp_1',
 'iso2022_jp_2',
 'iso2022_jp_2004',
 'iso2022_jp_3',
 'iso2022_jp_ext',
 'iso2022_kr',
 'latin_1',
 'iso8859_2',
 'iso8859_3',
 'iso8859_4',
 'iso8859_5',
 'iso8859_6',
 'iso8859_7',
 'iso8859_8',
 'iso8859_9',
 'iso8859_10',
 'iso8859_13',
 'iso8859_14',
 'iso8859_15',
 'iso8859_16',
 'johab',
 'koi8_r',
 'koi8_u',
 'mac_cyrillic',
 'mac_greek',
 'mac_iceland',
 'mac_latin2',
 'mac_roman',
 'mac_turkish',
 'ptcp154',
 'shift_jis',
 'shift_jis_2004',
 'shift_jisx0213',
 'utf_32',
 'utf_32_be',
 'utf_32_le',
 'utf_16',
 'utf_16_be',
 'utf_16_le',
 'utf_7',
 'utf_8',
 'utf_8_sig']

в Python 3.4 (96 кодировки)

['ascii',
 'big5',
 'big5hkscs',
 'cp037',
 'cp273',
 'cp424',
 'cp437',
 'cp500',
 'cp720',
 'cp737',
 'cp775',
 'cp850',
 'cp852',
 'cp855',
 'cp856',
 'cp857',
 'cp858',
 'cp860',
 'cp861',
 'cp862',
 'cp863',
 'cp864',
 'cp865',
 'cp866',
 'cp869',
 'cp874',
 'cp875',
 'cp932',
 'cp949',
 'cp950',
 'cp1006',
 'cp1026',
 'cp1125',
 'cp1140',
 'cp1250',
 'cp1251',
 'cp1252',
 'cp1253',
 'cp1254',
 'cp1255',
 'cp1256',
 'cp1257',
 'cp1258',
 'cp65001',
 'euc_jp',
 'euc_jis_2004',
 'euc_jisx0213',
 'euc_kr',
 'gb2312',
 'gbk',
 'gb18030',
 'hz',
 'iso2022_jp',
 'iso2022_jp_1',
 'iso2022_jp_2',
 'iso2022_jp_2004',
 'iso2022_jp_3',
 'iso2022_jp_ext',
 'iso2022_kr',
 'latin_1',
 'iso8859_2',
 'iso8859_3',
 'iso8859_4',
 'iso8859_5',
 'iso8859_6',
 'iso8859_7',
 'iso8859_8',
 'iso8859_9',
 'iso8859_10',
 'iso8859_11',
 'iso8859_13',
 'iso8859_14',
 'iso8859_15',
 'iso8859_16',
 'johab',
 'koi8_r',
 'koi8_u',
 'mac_cyrillic',
 'mac_greek',
 'mac_iceland',
 'mac_latin2',
 'mac_roman',
 'mac_turkish',
 'ptcp154',
 'shift_jis',
 'shift_jis_2004',
 'shift_jisx0213',
 'utf_32',
 'utf_32_be',
 'utf_32_le',
 'utf_16',
 'utf_16_be',
 'utf_16_le',
 'utf_7',
 'utf_8',
 'utf_8_sig']

в Python 3.5 (98 кодировки)

['ascii',
 'big5',
 'big5hkscs',
 'cp037',
 'cp273',
 'cp424',
 'cp437',
 'cp500',
 'cp720',
 'cp737',
 'cp775',
 'cp850',
 'cp852',
 'cp855',
 'cp856',
 'cp857',
 'cp858',
 'cp860',
 'cp861',
 'cp862',
 'cp863',
 'cp864',
 'cp865',
 'cp866',
 'cp869',
 'cp874',
 'cp875',
 'cp932',
 'cp949',
 'cp950',
 'cp1006',
 'cp1026',
 'cp1125',
 'cp1140',
 'cp1250',
 'cp1251',
 'cp1252',
 'cp1253',
 'cp1254',
 'cp1255',
 'cp1256',
 'cp1257',
 'cp1258',
 'cp65001',
 'euc_jp',
 'euc_jis_2004',
 'euc_jisx0213',
 'euc_kr',
 'gb2312',
 'gbk',
 'gb18030',
 'hz',
 'iso2022_jp',
 'iso2022_jp_1',
 'iso2022_jp_2',
 'iso2022_jp_2004',
 'iso2022_jp_3',
 'iso2022_jp_ext',
 'iso2022_kr',
 'latin_1',
 'iso8859_2',
 'iso8859_3',
 'iso8859_4',
 'iso8859_5',
 'iso8859_6',
 'iso8859_7',
 'iso8859_8',
 'iso8859_9',
 'iso8859_10',
 'iso8859_11',
 'iso8859_13',
 'iso8859_14',
 'iso8859_15',
 'iso8859_16',
 'johab',
 'koi8_r',
 'koi8_t',
 'koi8_u',
 'kz1048',
 'mac_cyrillic',
 'mac_greek',
 'mac_iceland',
 'mac_latin2',
 'mac_roman',
 'mac_turkish',
 'ptcp154',
 'shift_jis',
 'shift_jis_2004',
 'shift_jisx0213',
 'utf_32',
 'utf_32_be',
 'utf_32_le',
 'utf_16',
 'utf_16_be',
 'utf_16_le',
 'utf_7',
 'utf_8',
 'utf_8_sig']

Python 3.6 (98 кодировки)

['ascii',
 'big5',
 'big5hkscs',
 'cp037',
 'cp273',
 'cp424',
 'cp437',
 'cp500',
 'cp720',
 'cp737',
 'cp775',
 'cp850',
 'cp852',
 'cp855',
 'cp856',
 'cp857',
 'cp858',
 'cp860',
 'cp861',
 'cp862',
 'cp863',
 'cp864',
 'cp865',
 'cp866',
 'cp869',
 'cp874',
 'cp875',
 'cp932',
 'cp949',
 'cp950',
 'cp1006',
 'cp1026',
 'cp1125',
 'cp1140',
 'cp1250',
 'cp1251',
 'cp1252',
 'cp1253',
 'cp1254',
 'cp1255',
 'cp1256',
 'cp1257',
 'cp1258',
 'cp65001',
 'euc_jp',
 'euc_jis_2004',
 'euc_jisx0213',
 'euc_kr',
 'gb2312',
 'gbk',
 'gb18030',
 'hz',
 'iso2022_jp',
 'iso2022_jp_1',
 'iso2022_jp_2',
 'iso2022_jp_2004',
 'iso2022_jp_3',
 'iso2022_jp_ext',
 'iso2022_kr',
 'latin_1',
 'iso8859_2',
 'iso8859_3',
 'iso8859_4',
 'iso8859_5',
 'iso8859_6',
 'iso8859_7',
 'iso8859_8',
 'iso8859_9',
 'iso8859_10',
 'iso8859_11',
 'iso8859_13',
 'iso8859_14',
 'iso8859_15',
 'iso8859_16',
 'johab',
 'koi8_r',
 'koi8_t',
 'koi8_u',
 'kz1048',
 'mac_cyrillic',
 'mac_greek',
 'mac_iceland',
 'mac_latin2',
 'mac_roman',
 'mac_turkish',
 'ptcp154',
 'shift_jis',
 'shift_jis_2004',
 'shift_jisx0213',
 'utf_32',
 'utf_32_be',
 'utf_32_le',
 'utf_16',
 'utf_16_be',
 'utf_16_le',
 'utf_7',
 'utf_8',
 'utf_8_sig']

Python 3.7 (98 кодировок)

['ascii',
 'big5',
 'big5hkscs',
 'cp037',
 'cp273',
 'cp424',
 'cp437',
 'cp500',
 'cp720',
 'cp737',
 'cp775',
 'cp850',
 'cp852',
 'cp855',
 'cp856',
 'cp857',
 'cp858',
 'cp860',
 'cp861',
 'cp862',
 'cp863',
 'cp864',
 'cp865',
 'cp866',
 'cp869',
 'cp874',
 'cp875',
 'cp932',
 'cp949',
 'cp950',
 'cp1006',
 'cp1026',
 'cp1125',
 'cp1140',
 'cp1250',
 'cp1251',
 'cp1252',
 'cp1253',
 'cp1254',
 'cp1255',
 'cp1256',
 'cp1257',
 'cp1258',
 'cp65001',
 'euc_jp',
 'euc_jis_2004',
 'euc_jisx0213',
 'euc_kr',
 'gb2312',
 'gbk',
 'gb18030',
 'hz',
 'iso2022_jp',
 'iso2022_jp_1',
 'iso2022_jp_2',
 'iso2022_jp_2004',
 'iso2022_jp_3',
 'iso2022_jp_ext',
 'iso2022_kr',
 'latin_1',
 'iso8859_2',
 'iso8859_3',
 'iso8859_4',
 'iso8859_5',
 'iso8859_6',
 'iso8859_7',
 'iso8859_8',
 'iso8859_9',
 'iso8859_10',
 'iso8859_11',
 'iso8859_13',
 'iso8859_14',
 'iso8859_15',
 'iso8859_16',
 'johab',
 'koi8_r',
 'koi8_t',
 'koi8_u',
 'kz1048',
 'mac_cyrillic',
 'mac_greek',
 'mac_iceland',
 'mac_latin2',
 'mac_roman',
 'mac_turkish',
 'ptcp154',
 'shift_jis',
 'shift_jis_2004',
 'shift_jisx0213',
 'utf_32',
 'utf_32_be',
 'utf_32_le',
 'utf_16',
 'utf_16_be',
 'utf_16_le',
 'utf_7',
 'utf_8',
 'utf_8_sig']

в случае, если они имеют отношение к чьему-либо случаю использования, обратите внимание, что документы также перечисляют некоторые Python-специфические кодировки, многие из которых, по-видимому, в основном предназначены для использования внутренними устройствами Python или иным образом странно, как 'undefined' кодировка, которая всегда создает исключение, если вы пытаетесь ее использовать. Вы, вероятно, хотите полностью игнорировать их, если, как и вопрос-спрашивающий здесь, вы попытка выяснить, какая кодировка использовалась для некоторого текста, с которым вы столкнулись в реальном мире. Начиная с Python 3.6, список выглядит следующим образом:

["idna",
 "mbcs",
 "oem",
 "palmos",
 "punycode",
 "raw_unicode_escape",
 "rot_13",
 "undefined",
 "unicode_escape",
 "unicode_internal",
 "base64_codec",
 "bz2_codec",
 "hex_codec",
 "quopri_codec",
 "string_escape",
 "uu_codec",
 "zlib_codec"]

наконец, если вы хотите обновить мои таблицы выше для более новой версии Python, вот (грубый, не очень надежный) скрипт, который я использовал для их создания:

import requests
import lxml.html
import pprint

for version, url in [
    ('2.3', 'https://docs.python.org/2.3/lib/node130.html'),
    ('2.4', 'https://docs.python.org/2.4/lib/standard-encodings.html'),
    ('2.5', 'https://docs.python.org/2.5/lib/standard-encodings.html'),
    ('2.6', 'https://docs.python.org/2.6/library/codecs.html#standard-encodings'),
    ('2.7', 'https://docs.python.org/2.7/library/codecs.html#standard-encodings'),
    ('3.0', 'https://docs.python.org/3.0/library/codecs.html#standard-encodings'),
    ('3.1', 'https://docs.python.org/3.1/library/codecs.html#standard-encodings'),
    ('3.2', 'https://docs.python.org/3.2/library/codecs.html#standard-encodings'),
    ('3.3', 'https://docs.python.org/3.3/library/codecs.html#standard-encodings'),
    ('3.4', 'https://docs.python.org/3.4/library/codecs.html#standard-encodings'),
    ('3.5', 'https://docs.python.org/3.5/library/codecs.html#standard-encodings'),
    ('3.6', 'https://docs.python.org/3.6/library/codecs.html#standard-encodings'),
]:
    html = requests.get(url).text
    doc = lxml.html.fromstring(html)
    standard_encodings_table = doc.xpath(
        '//table[preceding::h3[.//text()[contains(., "Standard Encodings")]]][//th/text()="Codec"]'
    )[0]
    codecs = standard_encodings_table.xpath('.//td[1]/text()')
    print("## Python %s (%i encodings)" % (version, len(codecs)))
    print('<pre><code>' + pprint.pformat(codecs) + '</code></pre>')

Как прочитать файлы в Python и решить проблему с кодировками

На практике в реальных проектах Data Science часто приходится сталкиваться с чтением датасетов, а также записывать добытую в ходе вычислений информацию в файлы. Сегодня мы расскажем о работе с файлами в Python: чтение и запись, проблема с кодировками, добавление значений в конец файла, временные папки и файлы.

Открываем, а затем читаем или записываем

Предположим, у нас имеется файл, который нужно прочитать в Python. Для этого можно воспользоваться функцией open внутри контекстного менеджера:

with open('file.txt') as f:
    data = f.read() # содержимое файла

Таким же образом можно записать информацию в файл, указав w в качестве аргумента:

text = 'Hello'
with open('file.txt', 'w') as f:
    f.write(text)

Отметим некоторые особенности данной функции. Во-первых, для чтения файла мы не указывали никаких аргументов кроме имени файла, поскольку по умолчанию уже стоит режим чтения. Мы также не указывали явно, что это именно текстовый файл, а не бинарный, так как это тоже стоит по умолчанию. Для чтения и записи бинарных файлов добавляется b, например, rb или wb.

Во-вторых, мы использовали функцию open в контекстном менеджере. Можно обойтись и без него, но тогда после чтения или записи следует закрыть файл.

f = open('file.txt')
f.read()
f.close()

На открытие файла Python выделяет память, поэтому, чтобы избежать ее утечки, рекомендуется закрывать файлы.

Чтение файла с разной кодировкой

На многих операционных системах Python в качестве стандарта кодирования использует UTF-8, который также поддерживает кириллицу. Тем не менее, часто можно столкнуться с проблемами неправильной кодировки и получить распространенную ошибку вроде этой:

>>> f = open('somefile.txt', encoding='ascii')
>>> f.read()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/Python3.8/encodings/ascii.py", line 26, in decode
    return codecs.ascii_decode(input, self.errors)[0]
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position
12: ordinal not in range(128))

В примере указана кодировка ASCII, но файл закодирован в другом формате, поэтому и возникает такая ошибка. Решить ее можно тремя способами:

  1. Указать erorr=replace, который заменит нераспознанные символы знаком ?:
    >>> f = open('somefile.txt', encoding='ascii', errors='replace')
    >>> f.read()
    'H?llo py?ho?-school!'
    
  2. Указать erorr=ignore, который проигнорирует нераспознанные символы:
    >>> f = open('somefile.txt', encoding='ascii', errors='replace')
    >>> f.read()
    'Hllo pyho-school!'
    
  3. Указать правильную кодировку. Если текст на русском языке, то можно посмотреть кодировки с поддержкой кириллицы, которые есть в документации Python. Например, явно указать UTF-8 или cp1251:
    f = open('somefile.txt', encoding='utf-8')
    # или cp1251
    f = open('somefile.txt', encoding='cp1251')
    
Добавление в конец и запрет открытия файлов

Как мы уже отметили ранее, для записи текстового файла добавляется аргумент w. Но если вызвать метод write, он перепишет весь файл. Во многих случаях требуется добавить данные в конец файла. Тогда используется a вместо w:

text2 = 'world'
with open('file.txt', 'a') as f:
    f.write(text)
# Helloworld

Если файла не существует, то при a и при w он будет создан. Но чтобы не трогать существующие файлы, а создать новый, передается параметр x:

# 'x' не даст возможности открыть файл, так как он существует
>>> with open('file.txt', 'x') as f:
...    f.write(text2)
FileExistsError                           Traceback (most recent call last)

  FileExistsError: [Errno 17] File exists: 'file.txt'

# Поскольку file2.txt не существует, все OK
>>> with open('file2.txt', 'x') as f:
...    f.write(text2)
Временные файлы

Иногда бывает, что требуется создать файл или папку внутри Python-программы, а после ее закрытия их нужно удалить. Тогда пригодится стандартный модуль tempfile. Например, класс TemporaryFile создаст временный файл, который удалится после закрытия. Ниже пример в Python.

>>> from tempfile import TemporaryFile
>>> f = TemporaryFile("w+t")
>>> f.write("hello")
>>> f.seek(0)
>>> f.read()
'hello'
>>> f.close() # файл уничтожается
# либо в контекстном менеджере
    f.write(text2)

Обратите внимание на 3 вещи. Первое, мы явно передаем "w+t", чтобы записать как текстовый файл, поскольку по умолчанию стоит "w+b" для бинарных файлов. Второе, метод seek(0) используется для перехода на самый первый символ, поскольку чтение происходит с текущего указателя, а он стоит в конце (после буквы ‘o’ в слове ‘hello’). Поэтому не стоит переживать, что мы можем стереть предыдущую запись:

>>> f.seek(5) # переходим в конец
>>> f.read()
''
>>> f.write("world")
5
>>> f.seek(0) # переходим в начало
>>> f.read()
'helloworld'

Третье, файл TemporaryFile невидим для файловой системы, он используется только внутри Python, поэтому извне будет трудно его найти.

Именованные временные файлы

А вот объекты класса NamedTemporaryFile будут видны файловой системе, и найти месторасположение можно с помощью атрибута name:

>>> from tempfile import NamedTemporaryFile
>>> f = NamedTemporaryFile("w+t")
>>> f.name
'/tmp/tmp60djsgli'
>>> f.close()

Как можно заметить, файл называется tmp60djsgli. Для удобства можно явно указать его название и формат:

>>> f = NamedTemporaryFile("w+t", prefix="myfile", suffix=".txt")
>>> f.name
'/tmp/myfile7mxae0fi.txt'
Временные папки

Кроме временных файлов можно создавать временные папки. Для этого используется класс TemporaryDirectory:

>>> from tempfile import TemporaryDirectory
>>> d = TemporaryDirectory()
>>> d.name
'/tmp/tmp5eadqzz5'

Он также принимает в качестве аргументов prefix и suffix, а также может использоваться внутри контекстного менеджера Python.

 

В следующей статье поговорим о взаимодействии файловой системы и Python. А получить практические навыки работы с файлами на реальных проектах Data Science вы сможете на наших курсах по Python в лицензированном учебном центре обучения и повышения квалификации IT-специалистов в Москве.

Источники

  1. https://docs.python.org/3/library/functions.html#open
  2. https://docs.python.org/3/library/tempfile.html

Катастрофа Unicode в Python3 / Хабр

От переводчика: Armin Ronacher довольно известный разработчик в Python-сообществе(Flask,jinja2,werkzeug).
Он довольно давно начал своеобразный крестовый поход против Python3, но обвинить его в истерике и ретроградстве не так-то просто: его возражения продиктованы серьезным опытом разработки, он довольно подробно аргументирует свою точку зрения. Немного о терминологии:
coercion я перевел как принудительное преобразование кодировок, а byte string как байтовые строки, так как термин «сырые» строки(raw string) все же означает несколько иное.
«Историческое» примечание: в 2012 г. Армин предложил PEP 414, который содержал ряд мер по устранению проблем с Unicode, PEP подтвердили довольно быстро, однако воз и ныне там, так как нижеприведенный текст написан 5 января 2014 года

Все труднее становиться вести обоснованную дискуссию о различиях между Python 2 и 3, так как один язык уже мертв,
а второй активно развивается. Когда кто-либо начинает обсуждение поддержки Unicode в двух ветках Python — это весьма сложная тема. Вместо рассмотрения поддержки Unicode в двух версиях языка, я рассмотрю базовую модель обработки текста и байтовых строк.

В данном посте я покажу на примере решений разработчиков языка и стандартной библиотеки,
что Python 2 лучше подходит для работы с текстом и байтовыми строками.

С тех пор как мне пришлось сопровождать большое количество кода, который напрямую работал с преобразованием между байтовыми строками и Unicode, ухудшения, произошедшие в Python3, вызвали у меня много печали. Особенно меня раздражают материалы основной команды разработчиков python, которые призывают меня верить, что python 3 лучше 2.7.

Модель представления текста

Главное различие между Python 2 и Python 3 —базовые типы, существующие для работы со строками и байтовыми строками. В Python 3 мы имеем один строковый тип: str, который хранит данные в Unicode, и два байтовых типа: bytes и bytearray.

С другой стороны, в Python 2 у нас есть два строковых типа: str, который достаточен для любых целей и задач, ограниченных строками в кодировке ASCII + некоторыми неопределенными данными, превышающими интервал в 7 бит. Вместе с типом str у Python2 есть тип данныхunicode, эквивалентный типу данных str Python 3. Для работы с байтами в Python 2 есть один тип:bytearray, взятый из Python 3. Присмотревшись к ситуации, вы можете заметить, что из Python 3 кое-что удалили: поддержку строковых данных не в юникоде.Компенсацией жертвоприношения стал хешируемый байтовый тип данных(bytes). Тип данных bytarray изменяемый, а поэтому он не может быть хеширован. Я очень редко, использую бинарные данные как ключи словаря, а потому возможность или невозможность хеширования бинарных данных не кажется мне очень серьезной. Особенно в Python 2, так как байты могут быть без каких-либо проблем помещены в переменную типа str.

Потерянный тип

Из Python 3 исключают поддержку байтовых строк, которые в ветке 2.x были типом str. На бумаге в этом решении нет ничего плохого. С академической точки зрения строки, всегда представленные в юникоде, это прекрасно. И это действительно так, если целый мир — это ваш интерпретатор. К сожалению, в реальном мире, все происходит по-другому: вы вынуждены регулярно работать с разными кодировками, в этом случае подход Python 3 к работе со строками трещит по швам.

Буду честен перед вами: то как Python 2 обрабатывает Unicode провоцирует ошибки, и я полностью одобряю улучшения обработки Unicode. Моя позиция в том, что, то как это делается в Python 3, является шагом назад и порождает еще больше ошибок, а потому я абсолютно ненавижу работать с Python 3.

Ошибки при работе с Unicode

Прежде чем я погружусь в детали, мы должны понять разницу поддержки Unicode в Python 2 и 3,
а так же то, почему разработчики приняли решение поменять механизм поддержки Unicode.

Изначально Python 2 как и многие иные языки до него создавался без поддержки обработки сток разных кодировок.
Строка и есть строка, она содержит байты. Это требовало от разработчиков корректно работать с различными
кодировками вручную. Это было вполне приемлемо для многих ситуаций. Многие годы веб-фреймворк Django
не работал с Unicode, а использовал исключительно байтовые строки.

Тем временем Python 2 годами улучшал внутреннюю поддержку Unicode. Улучшение поддержки Unicode
позволяло использовать его для единообразного представления данных в различных кодировках.

Подход к обработке строк, использующих определенную кодировку, в Python 2 довольно прост:
вы берете строку (байтовую), которую вы могли получить откуда угодно, а затем преобразуете
ее из той кодировки, которая характерна для источника строки(метаданные, заголовки, иные)
в строку Unicode. Став Unicode строкой, она поддерживает все те же операции
что и байтовая, но теперь она может хранить больший диапазон символов.
Когда вам необходимо передать строку на обработку куда-либо еще, то вы снова
преобразуете ее в ту кодировку, которая используется принимающей стороной,
и перед нами вновь байтовая строка

Какие же особенности связаны с таким подходом? Для того, чтобы это работало на уровне ядра языка,
Python 2 должен предоставлять способ перехода из мира без Unicode в прекрасный мир с Unicode.
Это возможно благодаря принудительному преобразованию байтовых и небайтовых строк. Когда это происходит
и как этот механизм работает?

Основной момент заключается в том, что когда байтовая строка участвует в одной операции с Unicode строкой,
то байтовая строка преобразуется в Unicode строку при помощи неявного процесса декодирования строки, который использует кодировку «по умолчанию». Данной кодировкой по умолчанию считается ASCII. Python предоставлял возможность менять кодировку по умолчанию, используя один модуль, но теперь из модуля site.py удалили функции для изменения кодировки по умолчанию, она устанавливается в ASCII. Если запустить интерпретатор с флагом -s, то функция sys.setdefaultencoding будет вам доступна и вы сможете поэкспериментировать, чтобы выяснить что произойдет, если вы выставите кодировкой по умолчанию UTF-8. В некоторых ситуациях при работе с кодировкой по умолчанию могут возникнуть проблемы:

1. неявное задание и преобразование кодировки при конкатенации:

>>> "Hello " + u"World"
u'Hello World'

Здесь левая строка преобразуется, используя кодировку «по умолчанию», в Unicode строку. Если строка содержит не ASCII символы, то при нормальной ситуации выполнения программы преобразование останавливается с выбросом исключения UnicodeDecodeError, так как кодировка по умолчанию — ASCII

2. Неявное задание и преобразование кодировки при сравнении строк


>>> "Foo" == u"Foo"
True

Это звучит опаснее чем есть на самом деле. Левая часть преобразуется в Unicode, а затем происходит сравнение. В случае, если левая сторона не может быть преобразована, интерпретатор выдает предупреждение, а строки считаются неравными(возвращается False в качестве результата сравнения). Это вполне здравое поведение, если даже при первом знакомстве с ним так не кажется.

3. Явное задание и преобразование кодировки, как часть механизма с использованием кодеков.

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

Явное преобразование кодировки при работе с кодеками выглядит так:


>>> "foo".encode('utf-8')
'foo'

Это строка, очевидно, является байтовой строкой. Мы требуем ее преобразовать в UTF-8. Само по себе эnо бессмысленно, так как UTF-8 кодек преобразует строку из Unicode в байтовую строку с кодировкой UTF-8. Как же это работает? UTF-8 кодек видит, что строка не является Unicode строка, а поэтому сначала выполняется принудительное преобразование к Unicode. Пока «foo» только ASCII данные и кодировка по умолчанию ASCII, принудительное преобразование происходит успешно, а уже после этого Unicode строка u«foo» преобразуется в UTF-8.

Механизм кодеков

Теперь вы знаете что Python 2 имеет два подхода к представлению строк: байтами и Unicode. Преобразование между этими представлениями осуществляется при помощи механизма кодеков. Данный механизм не навязывает схему преобразования Unicode->byte или на нее похожую. Кодек может производить преобразование byte->byte или Unicode->Unicode. Фактически система кодеков может реализовывать преобразование между любыми типами Python. Вы можете иметь JSON кодек, который производит преобразование строки в сложный Python объект на ее основе, если сочтете, что такое преобразование вам необходимо.

Такое положение дел может вызвать проблемы с пониманием механизма, начиная с его основ. Примером этого может быть кодек с названием ‘undefined’, который может быть установлен в качестве кодировки по умолчанию. В этом случае любые принудительные преобразования кодировок строк будут отключены:


>>> import sys
>>> sys.setdefaultencoding('undefined')

>>> "foo" + u"bar"
Traceback (most recent call last):
    raise UnicodeError("undefined encoding")
UnicodeError: undefined encoding
     

И как же в Python 3 решают проблему с кодеками? Python 3 удаляет все кодеки, которые не выполняют преобразования вида: Unicode<->byte, а кроме того уже ненужные сейчас метод байтовых строк .encode() и строковый метод .decode(). Это очень плохое решение, так как было очень
много полезных кодеков. Например очень распространено использовать преобразование с помощью hex кодека в Python 2:


>>> "\x00\x01".encode('hex')
'0001'  


Пока вы можете сказать, что в данном конкретном случае задача может быть решена при помощи модуля подобного binascii, но проблема более глубока, модули с кодеками доступны отдельно. Например библиотеки, реализующие чтение из сокетов, используют кодеки для частичного преобразования данных из потоков данных библиотеки zlib:

>>> import codecs
>>> decoder = codecs.getincrementaldecoder('zlib')('strict')
>>> decoder.decode('x\x9c\xf3H\xcd\xc9\xc9Wp')
'Hello '
>>> decoder.decode('\xcdK\xceO\xc9\xccK/\x06\x00+\xad\x05\xaf')
'Encodings'


В конце концов, проблема была признана и в Python 3.3 восстановили эти кодеки. Однако сейчас мы снова вводим пользователя в неразбериху, так как кодеки до вызова функций не предоставляют метаинформации о тех типа, которые они могут обработать. По этой причине Python теперь может выбрасывать следующие исключения:
 
>>> "Hello World".encode('zlib_codec')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' does not support the buffer interface

(Обратите внимание, что кодек теперь называется zlib_codec вместо zlib, так как Python 3.3 не сохранил старых обозначений для кодеков)

А что произойдет, если мы вернем назад метод .encode() для байтовых строк, например? Это легко проверить даже без хаков интерпретатора Python. Напишем функцию с аналогичным поведением:

import codecs

def encode(s, name, *args, **kwargs):
    codec = codecs.lookup(name)
    rv, length = codec.encode(s, *args, **kwargs)
    if not isinstance(rv, (str, bytes, bytearray)):
        raise TypeError('Not a string or byte codec')
    return rv

Теперь мы можем использовать эту функцию как замену метода .encode() байтовых строк:

>>> b'Hello World'.encode('latin1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'bytes' object has no attribute 'encode'

>>> encode(b'Hello World', 'latin1')
Traceback (most recent call last):
  File "<stdin>", line 4, in encode
TypeError: Can't convert 'bytes' object to str implicitly

Ага! Python 3 уже умеет работать с такой ситуацией. Мы получаем красивое оповещение об ошибке. Я считаю, что даже “Can’t convert ‘bytes’ object to str implicitly” гораздо лучше и понятней чем “’bytes’ object has no attribute ‘encode’”.

Почему бы не вернуть эти методы преобразования кодировки(encode и decode) назад? Я действительно не знаю и не думаю больше об этом. Мне уже многократно объясняли что я ничего не понимаю и я не понимаю новичков, или, то что «текстовая модель» изменилась и мои требования к ней бессмысленны.

Байтовые строки потеряны

Теперь вслед за регрессией системы кодеков изменились и строковые операции: они определены лишь для строк Unicode. На первый взгляд это выглядит вполне разумно, но на самом деле это не так. Раньше интерпретатор имел реализации для операций над байтовыми и Unicode строками. Этот подход был совершенно очевиден дял программистов, если объекту нужно было иметь представление в виде байтовой или Unicode строки, определялось два метода:__str__ and __unicode__. Да, конечно, использовалось принудительное изменение кодировки, которое смущало новичков, но зато у нас был выбор.

Почему это полезно? Потому что, к примеру, если вы работаете с низкоуровневыми протоколами, вам часто необходимо иметь дело с числами в определенном фоормате внутри байтовой строки.

Собственная система контроля версий, используемая разработчиками Python, не работает на Python 3, потому что годами команда разработки Python не хочет вернуть возможность форматирования для байтовых строк.

Все вышеописанное показывает: модель обработки строковых данных Python 3 не работает в реальном мире. К примеру в Python 3 «обновили» некоторые API, сделав их работающими только с Unicode, а потому они полностью непригодны для применения в реальных рабочих ситуациях. К примеру теперь вы не можете больше анализировать байты с помощью стандартной библиотеки, но только URL. Причина этого в неявном предположении, что все URL представлены лишь в Unicode (при таком положении дел вы уже не сможете работать с почтовыми сообщениями в не Unicode кодировке, если не будете полностью игнорировать существование бинарных вложений в письмо).

Раньше такое было довольно легко исправить, но так как ныне байтовые строки потеряны для разработчиков, библиотека обработки URL имеет ныне две реализации. Одна для Unicode, а вторая для байтовых объектов. Две реализации для одной и той же функции ведут к тому что результат обработки данных может быть очень разным:


>>> from urllib.parse import urlparse
>>> urlparse('http://www.google.com/')
ParseResult(scheme='http', netloc='www.google.com',
            path='/', params='', query='', fragment='')
>>> urlparse(b'http://www.google.com/')
ParseResultBytes(scheme=b'http', netloc=b'www.google.com',
                 path=b'/', params=b'', query=b'', fragment=b'')


Выглядит достаточно похоже? Вовсе нет, потому что в результате мы имеем совершенно разные типы данных у результата операции.
Один из них это кортеж строк, второй больше похож на массив целых чисел. Я уже писал об этом ранее и подобное состояние вызывает у меня страдания. Теперь написание кода на Python доставляет мне серьезный дискомфорт или становится крайне неэффективным, так как вам теперь приходится пройти через большое количество преобразований кодировок данных. Из-за этого становится очень сложно писать код, реализующий все необходимые функции. Идея что все Unicode очень хороша в теории, но полностью неприменима на практике.

Python 3 пронизан кучей костылей для обработки ситуаций, где обрабатывать Unicode невозможно, а у таких как я, которые много работают с такими ситуациями, все это вызывает жуткое раздражение.

Наши костыли не работают

Поддержка Unicode в ветке 2.х неидеальна и далека от идеала. Это отсутсвующие API, проблемы, приходящие с разных сторон, но мы как программисты делали все это рабочим. Многие методы, которыми мы это делали ранее больше невозможно применить в Python 3, а некоторые API будут изменены, чтобы хорошо работать с Python 3.

Мой любимый пример это обработка файловых потоков, которые могли быть как байтовыми, так и текстовыми, но не было надежного метода определить какой перед нами тип потока. Трюк, который я помог популяризировать это чтение нулевого количества байт из потока для определения его типа. Теперь этот трюк не работает. К примеру передача объекта запроса библиотеки urllib функции Flask, которая обрабатывает JSON, не работает в Python 3, но работает в Python 2:

>>> from urllib.request import urlopen
>>> r = urlopen('https://pypi.python.org/pypi/Flask/json')
>>> from flask import json
>>> json.load(r)
Traceback (most recent call last):
  File "decoder.py", line 368, in raw_decode
StopIteration

В ходе обработки выбрашенного исключения выбрасывается еще одно:

 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: No JSON object could be decoded

И что же?

Кроме тех проблем, что я описал выше у Python 3 с поддержкой Unicode есть и куча других проблем. Я начал отписываться от твиттеров разработчиков Python потому что мне надоело читать какой Python 3 замечательный, так как это противоречит моему опыту. Да, в Python 3 много плюшек, но то как поступили с обработкой байтовых строк и Unicode к ним не относится.

(Хуже всего то, что многие действительно крутые возможности Python 3 обычно столь же хорошо работают и в Python 2. Например yield from, nonlocal, поддержка SNI SSL и т.д. )

В свете того, что только 3% разработчиков Python активно используют Python 3, а разработчики Python в Twitter громогласно заъявляют что миграция на Python 3 идет как и планировалось, я испытываю разочарование, так как подробно описывал свой опыт с Python 3 и как от последнего хочется избавиться.

Я не хочу это делать сейчас, но желаю, чтобы команда разработчиков Python 3 чуть больше прислушалась к мнению сообщества. Для 97% из нас, Python 2, уютненький мирок, в котором мы работали годами, а потому довольно болезненно воспринимается ситуация, когда к нам приходят и заъявляют: Python 3 — прекрасен и это не обсуждается. Это и просто не так в свете множества регрессий. Вместе с теми людьми, которые начинают обсуждать Python 2.8 и Stackless Python 2.8 я не знаю что такое провал, если это не он.

Кодировки, юникод; Работа с кодировками и юникодом в Python

Кодировки (общее)

Кодировки — это то, счем мы имеем дело каждый день, работая с компьютером, планшетом, кпк и Т.Д. Среднестатистический пользователь сталкивается с кодировками только тогда, когда на экране вместо текста, вылазят курказябры и разные умляуты. Все это признак, что текст закодированный в одной кодировке, пытаются прочесть при помощи другой кодировки. Чаще всего это происходит в текстовых файлах, на интернет страницах или в текстах писем почтовиков.
Какие названия кодировок чаще всего попадаются нам на глаза? Это utf-8, cp1251, ascii. Реже — cp866, koi8-r, utf-16, а также такое понятие как unicode. В рамках данной темы, я не стану писать о непосредственной тематике кодировок, принципов их создания и работы. Это не является задачей этой темы. Для углубления в понятие кодировок, я дам несколько ссылок. Кпримеру перевод одного из хороших материалов по тематике кодировок, можно посмотреть тут. Еще подробно и последовательно про кодировки рассказываетсятут.

Я рекомендую вдумчиво изучить материалы по данным ссылкам, Т.К. Всех начинающих программистов, работа с кодировками, нередко вводит в ступор.

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

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

Давайте договоримся, условно, что код 0101, отвечает за русскую букву «а», а код 0011, за букву «б». Да простят меня за такое читающие сие. Теперь представим 3 разных кодировки. Скажем cp1251, koi8-r и cp866. Я там сказал, что какие-то коды отвечают за буквы? Давайте уточним, в кодировке cp1251, код 0101, отвечает за букву «а», а код 0011, за букву «б» и у нас в тексте записано слово «баба» двоичным кодом. Тоесть, когда мы будем использовать кодировку cp1251 и нам встретится слово в двоичном коде вида «0011 0101 0011 0101», кодировка по этим кодам, преобразует нам эти коды в слово с букв «баба». Все хорошо, но продолжим…

Скажем, что в кодировке koi8-r, кодам 0101 и 0011, соответствуют буквы «ю» и «я». Что произойдет, если мы попробуем прочесть этоже слово «0011 0101 0011 0101», при помощи кодировки koi8-r? Мы получим слово из букв «яюяю».

Допустим, что кодировка cp866, содержит в себе соответствие кодам «0101» и «0011», для букв «у» и «х». Если мы попробуем прочесть слово записанное двоичным кодом вида «0011 0101 0011 0101» при помощи кодировки cp866, мы получим слово «хуху».

Немножко проясняется? То, что мы назваем кодировкой, всего лишь таблица, где хранятся условные двоичные коды и символы(буквы), которые соответствуют этим кодам в конкретной кодировке. Историю о том, почему так получилось, читаем все по тем же ссылкам, которые я давал выше.

Какой мы можем сделать общий вывод? Если у нас будет текст, который будет содержать себе буквы в двоичном коде и будет предназначен для прочтения кодировкой cp1251, а мы попробуем прочесть его при помощи другой кодировки, мы получим совершенно нечитаемый и неправильный набор букв. Также помним про то, что далеко не все кодировки содержат в себе русские буквы. Кпримеру кодировка ascii русских букв не имеет вовсе, да что там, очень мало кодировок, которые содержат в себе русские символы. И при попытке прочесть текст, который закодирован cp1251 при помощи какой-то индусской кодировки, мы получим айсуконфлексы, юсуконфлексы и другие нечитаемые псевдографические умляуты.

Как быть? Если вы удосужились прочесть что-то по ссылкам выше, вы должны были прочесть про такое явление, как юникод(unicode). Это символьное отображение всех символов, которые существуют в письменности на земле. Кпримеру буква «а», будет записана, как «\u0430», а буква «Б», как «\u0431». Каждый символ имеет в юникоде свое обозначение. Акцентирую ваше внимание, что юникод не кодировка — это способ символьного отображения других символов, где каждый символ имеет строго свое индивидуальное обозначение. Из этого следует вывод, что при необходимости мы можем воспользоваться таблицей юникода и закодировать соответствующие символы(буквы) в двоичный код, который будет соответствовать выбранной кодировке. Также мы можем раскодировать символы с любой кодировки в юникод. Приметивный пример этого может выглядеть примерно так:

Допустим буква «а» в cp1251, записана двоичным кодом «0101». Для koi8-r — «1001». в юникоде — «\u0430».

Раскодируем букву «а» с cp1251 в юникод и закодируем в cp866.

(0101 -> \u0430 -> 1001)

Нам ненужно помнить какие-то соответствия, за нас это помнят таблицы, нам нужно всего лишь помнить, что существуют разные кодировки и текст закодированный для одной кодировки, нельзя читать при помощи другой.

Мы можем раскодировать текст с какой-то кодировки в универсальный юникод, где каждый символ будет иметь индивидуальное обозначение.

Мы можем закодировать текст из юникода в необходимую нам кодировку.

Кстати, вы могли заметить, что почти всегда, курказябрами становится русский, украинский, одним словом текст, который состоит не из латинских(английских) букв и символов. В то время, как английский текст, почти всегда, остается читаемым. Все это потому, что практически все кодировки были построены на основании ascii и коды для английских символов в них совпадают.

Глотком свежего воздуха, стало создание кодировки utf-32 на базе юникодовой таблицы. Историю сего явления, вы тоже можете прочесть по ссылкам выше. По определенным причинам, кодировку utf-32 убрали с горизонта и создали кодировку utf-16. Боюсь повториться, но в силу еще кое-каких причин, была создана кодировка utf-8 и она медленно вытеснила кодировку utf-16. Сейчас де-факто utf-8 является стандартом для кодирования текстов. Конечно, стандарт в нашем мире сотен кодировок — это хорошо, но часто встречаются проприетарные кодировки, которые использовались ранее и в в каких-то случаях так и не изменились. Хорошим примером этого, может стать windows, со своей кодировкой cp1251 для общего окружения и cp866, для работы с консолью. Тотже linux, который ранее использовал кодировку koi8-r, уже давно перешел на utf-8. macos тоже использует кодировку utf-8. А windows попрежнему хватается за мультикодировочность и не использует по умолчанию utf-8.

Последнее, что я скажу про кодировки вобщем, что мы можем наблюдать кодировку utf-8 с bom и без bom. В некоторых системах, чтение байтов с двоичным кодом, которые отвечают за какие-то символы в кодировках, может производиться слева на право, а в каких-то справа на лево. Наша буква «а», записанная двоичнім кодом «0101», может читаться слева на право «0101», также и справа на лево «1010», что дает нам несколько разный набор нулей и единиц. Если мы запишем эту букву в системе, которая читает этот код слева на право, а попытаемся прочесть в системе, которая читает этот код справа на лево, мы получим курказябры, умляуты, вопросительные знаки и Т.Д. Символ bom — это первый символ в текстовом пространстве, который сообщает, в каком порядке необходимо читать наш код, что спасает нас от разночтения в разных операционных системах. Для начала, я советую использовать utf-8 без bom, что избавит нас от необходимости учитывать первый символ bom. У нас ничего страшного, без символа bom, не произойдет.

Юникод и кодировки в Python

Для начала учтем то, что я буду выдавать дальнейшую информацию исходя из того, что все это происходит под OS Windows, но постараюсь давать необходимые комментарии в местах, где это будет актуально для других OS.

Нам необходимо запомнить, что все, что происходит в консоли, происходит в кодировке cp866. Это актуально для Windows. При использовании Linux и MacOS, консоль будет в кодировке utf-8. Определенные шаги, которые мы будем совершать с кодировками в Python, будут актуальны для Windows и неактуальны для других систем, и наоборот.

При создании файла, в котором мы будем писать код нашей программы, первой строкой мы вписываем следующее:

#-*- coding:utf-8 -*-

Это сообщит Python, что содержимое файла, хранится в кодировке utf-8. Никогда не пренебрегайте этой строкой, она очень важна!

Дальнейшие пляски вокруг создания юникодовых литералов и кодирования/декодирования ввода и вывода данных в консоли, будут актуальны для OS Windows. Консоль MacOs и Linux используют кодировку utf-8, что позволяет работать с русским текстом без кодирования/декодирования строк. Правда, что в некоторых случаях при разнообразных настройках OS Linux (Чаще) и в MacOs (реже), мы можем получать неожиданные результаты работы с кодировками. В случаях совсем уж непонятных результатов, мы методом тыка пытаемся определить, что же не так, а еще лучше гуглим на тему кодировок в необходимой OS, а также кодировок в определенной OS при программировании на Python.

Для начала, мы поговорим о юникоде в Python. Текст в юникоде имеет свой тип данных «unicode». Создать юникодовый литерал(строку), можно несколькими способами.

Во-первых, мы можем указать модификатор «u» перед кавычками литерала «» или »:

>>> u’а’

u’\u0430′

>>>

>>> a = u’а’

>>> type(a)

>>>

Если мы не укажем модификатор «u», мы получим строчный литерал типа str в кодировке консоли: Для Windows — cp866, для остальных OS, с большой долей вероятности, utf-8.

>>> b = ‘а’

>>> b

‘\xa0’

>>> type(b)

>>>

Как видим, при попытке вывести русскую букву без использования оператора print, оканчивается выводом некой символьной записи этой буквы, но если мы воспользуемся оператором print, все будет нормально:

>>> a

u’\u0430′

>>> b

‘\xa0’

>>> print a

а

>>> print b

а

>>>

Нам необходимо запомнить, что оператор print, автоматически пытается вывести нам текст, подразумевая кодировку консоли — cp866 для Windows и utf-8 для других OS. Также, она автоматически кодирует unicode в кодировку консоли, что позволяет правильно отображать юникодовые литералы. И естественно, если нам попадется текст в кодировке cp1251, а мы попытаемся вывести его оператором print, мы получим курказябры, Т.К. print попытается вывести текст, ориентируясь на то, что он записан в кодировке консоли, что приводит нас к тому, что текст должен быть либо в кодировке, соответствующей кодировке консоли, либо в юникоде, либо мы должны явно раскодировать неподходящую для вывода кодировку в юникод.

В теории, если мы создадим юникодовый литерал, через print, мы должны получить нормальный читаемый текст, но как показала практика, в каких-то случаях юникодовый литерал, даже при использовании оператора print, вылазит курказябрами. Опять таки, в теории все, у кого консоль находится в utf-8, могут вовсе не забивать себе голову использованием всего богатства кодирований/декодирований для ввода и вывода данных в консоли. Для теста, как оно работать будет, выполните следующее (Актуально для MacOs и Linux):

a = ‘Привет’

print a

В теории должно выйти на экран читаемым текстом.

a = u»Привет»

print a

По хорошему, тоже должно выйти читаемым текстом, но полевые испытания показали, что почему-то невсегда.

a = «Привет».decode(‘utf-8’)

print a

Посути, мы ввели текст в utf-8 и декодировали его в unicode, Т.Е. Практически повторили предыдущий эксперимент, но тут мы дали строку в utf-8 и явно декодировали ее в unicode.

a = u»Привет».encode(‘utf-8’)

print a

Тут мы создали юникодовый литерал и явно преобразовали его в utf-8. Скорее всего, русский текст будет выводиться хорошо и без юникодового литерала и разных там кодирований/декодирований. Но также юникодовый литерал, должен правильно выводиться при использовании оператора print.

Еще один способ создания юникодового литерала, заключается в использовании функции преобразования типов unicode(), где первым аргументом мы передаем строчный литерал, а вторым указываем его текущую кодировку. На выходе получим юникодовый литерал:

>>> a = unicode(‘Привет’, ‘cp866’)

>>> a

u’\u041f\u0440\u0438\u0432\u0435\u0442′

>>> print a

Привет

>>> type(a)

>>>

Методы кодирования/декодирования

Для объекта строки и юникода, есть существуют методы, позволяющие кодировать и декодировать данные. Метод decode() для строчного литерала, позволит декодировать его из текущей кодировки в юникод, создав тем самым объект unicode.

>>> a

‘\xaf\xe0\xa8\xa2\xa5\xe2’

>>> type(a)

>>> a = a.decode(‘cp866’)

>>> a

u’\u043f\u0440\u0438\u0432\u0435\u0442′

>>> type(a)

>>>

Метод encode(), позволяет закодировать юникодовый литерал в строчный, явно указав необходимую кодировку.

>>> a = u’Привет’

>>> a

u’\u041f\u0440\u0438\u0432\u0435\u0442′

>>> type(a)

>>> a = a.encode(‘cp866’)

>>> a

‘\x8f\xe0\xa8\xa2\xa5\xe2’

>>> type(a)

>>>

Подведем итоги

Мы неможем прочесть текст, закодированный в одной кодировке при помощи другой. Мы пытаемся все держать в юникоде, чтобы проще было ориентироваться в том, что происходит в рамках нашего кода, Т.Е. Все входящие данные, мы преобразовываем в юникодовый литерал, а при необходимости вернуть эти данные куда-то, мы явно кодируем их в необходимую кодировку. Если вы попытаетесь заставить взаимодействовать между собой данные в разных кодировках или данные закодированные в какой-то кодировке и данные в юникоде, в лучшем случае выпадет трекбек, в худшем трекбека небудет, но и работать тоже ничего небудет.

Определение кодировки текста в Python

150

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Theme: Overlay by Kaira Extra Text
Cape Town, South Africa