Разное

Кодировки 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

автор: Community

8 ответов


правильное обнаружение кодировки все время невозможно.

(из CHARDET FAQ:)

однако некоторые кодировки оптимизированы
для конкретных языков и языков
они не случайны. Некоторый символ
последовательности всплывают все время, в то время как
другие последовательности не имеют смысла. Ля
человек, свободно владеющий английским языком, который открывает
газета и находит » txzqJv 2!dasd0a
QqdKjvz » мгновенно распознает, что
это не английский (даже если это
полностью состоит из английских букв).
Изучая множество «типичных» текстов,
компьютерный алгоритм может имитировать это
вид беглости и сделать образованный
угадайте язык текста.

есть chardet библиотека, которая использует это исследование, чтобы попытаться определить кодировку. chardet-это порт кода автоматического обнаружения в Mozilla.

вы также можете использовать UnicodeDammit. Он попробует следующее методы:

  • кодировка, обнаруженная в самом документе: например, в объявлении XML или (для HTML-документов) метатеге http-equiv. Если Beautiful Soup находит такую кодировку в документе, он снова анализирует документ с самого начала и дает новую кодировку попробовать. Единственное исключение — если вы явно указали кодировку, и эта кодировка действительно работала: тогда она будет игнорировать любую кодировку, которую она найдет в документе.
  • An кодировка понюхала, посмотрев на первые несколько байтов файла. Если кодировка обнаружена на этом этапе, это будет одна из кодировок UTF -*, EBCDIC или ASCII.
  • кодировка обнюхана chardet библиотека, если она установлена.
  • UTF-8
  • Windows-1252

другой вариант для разработки кодировки-использовать
libmagic (который является кодом
command). Существует изобилие
доступны привязки python.

привязки python, которые живут в дереве источника файлов, доступны как
python-magic (или python3-магия)
пакет Debian. Если можно определить кодировку файла, выполнив:

import magic

blob = open('unknown-file').read()
m = magic.open(magic.MAGIC_MIME_ENCODING)
m.load()
encoding = m.buffer(blob)  # "utf-8" "us-ascii" etc

есть с одинаковыми именами, но несовместимо,python-magic пакет pip на pypi, который также использует libmagic. Он также может получить кодировку, выполнив:

import magic

blob = open('unknown-file').read()
m = magic.Magic(mime_encoding=True)
encoding = m.from_buffer(blob)

43

автор: Hamish Downer


Некоторые стратегии кодирования, пожалуйста, раскомментируйте по вкусу :

#!/bin/bash
#
tmpfile=
echo '-- info about file file ........'
file -i $tmpfile
enca -g $tmpfile
echo 'recoding ........'
#iconv -f iso-8859-2 -t utf-8 back_test.xml > $tmpfile
#enca -x utf-8 $tmpfile
#enca -g $tmpfile
recode CP1250..UTF-8 $tmpfile

вы можете проверить кодировку, открыв и прочитав файл в виде цикла… но сначала вам может потребоваться проверить размер файла:

encodings = ['utf-8', 'windows-1250', 'windows-1252' ...etc]
            for e in encodings:
                try:
                    fh = codecs.open('file.txt', 'r', encoding=e)
                    fh.readlines()
                    fh.seek(0)
                except UnicodeDecodeError:
                    print('got unicode error with %s , trying different encoding' % e)
                else:
                    print('opening the file with encoding:  %s ' % e)
                    break              

вот пример чтения и принятия по номиналу a chardet кодировка предсказание, чтение n_lines из файла в случае, если он большой.

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

def predict_encoding(file_path, n_lines=20):
    '''Predict a file's encoding using chardet'''
    import chardet

    # Open the file as binary data
    with open(file_path, 'rb') as f:
        # Join binary lines for specified number of lines
        rawdata = b''.join([f.readline() for _ in range(n_lines)])

    return chardet.detect(rawdata)['encoding']

10

автор: ryanjdillon


# Function: OpenRead(file)

# A text file can be encoded using:
#   (1) The default operating system code page, Or
#   (2) utf8 with a BOM header
#
#  If a text file is encoded with utf8, and does not have a BOM header,
#  the user can manually add a BOM header to the text file
#  using a text editor such as notepad++, and rerun the python script,
#  otherwise the file is read as a codepage file with the 
#  invalid codepage characters removed

import sys
if int(sys.version[0]) != 3:
    print('Aborted: Python 3.x required')
    sys.exit(1)

def bomType(file):
    """
    returns file encoding string for open() function

    EXAMPLE:
        bom = bomtype(file)
        open(file, encoding=bom, errors='ignore')
    """

    f = open(file, 'rb')
    b = f.read(4)
    f.close()

    if (b[0:3] == b'\xef\xbb\xbf'):
        return "utf8"

    # Python automatically detects endianess if utf-16 bom is present
    # write endianess generally determined by endianess of CPU
    if ((b[0:2] == b'\xfe\xff') or (b[0:2] == b'\xff\xfe')):
        return "utf16"

    if ((b[0:5] == b'\xfe\xff\x00\x00') 
              or (b[0:5] == b'\x00\x00\xff\xfe')):
        return "utf32"

    # If BOM is not provided, then assume its the codepage
    #     used by your operating system
    return "cp1252"
    # For the United States its: cp1252


def OpenRead(file):
    bom = bomType(file)
    return open(file, 'r', encoding=bom, errors='ignore')


#######################
# Testing it
#######################
fout = open("myfile1.txt", "w", encoding="cp1252")
fout.write("* hi there (cp1252)")
fout.close()

fout = open("myfile2.txt", "w", encoding="utf8")
fout.write("\u2022 hi there (utf8)")
fout.close()

# this case is still treated like codepage cp1252
#   (User responsible for making sure that all utf8 files
#   have a BOM header)
fout = open("badboy.txt", "wb")
fout.write(b"hi there.  barf(\x81\x8D\x90\x9D)")
fout.close()

# Read Example file with Bom Detection
fin = OpenRead("myfile1.txt")
L = fin.readline()
print(L)
fin.close()

# Read Example file with Bom Detection
fin = OpenRead("myfile2.txt")
L =fin.readline() 
print(L) #requires QtConsole to view, Cmd.exe is cp1252
fin.close()

# Read CP1252 with a few undefined chars without barfing
fin = OpenRead("badboy.txt")
L =fin.readline() 
print(L)
fin.close()

# Check that bad characters are still in badboy codepage file
fin = open("badboy.txt", "rb")
fin.read(20)
fin.close()

Это, в принципе, невозможно определить кодировку текстового файла, в общем случае. Так что нет, нет стандартной библиотеки Python, чтобы сделать это за вас.

Если у вас есть более конкретные знания о текстовом файле (например, что это XML), могут быть библиотечные функции.

0

автор: Martin v. Löwis


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

0

автор: Martin Thurau


в зависимости от вашей платформы, я просто предпочитаю использовать Linux shell


4.9.2 Стандартные кодировки

ascii 646, us-ascii Английский
big5 big5-tw, csbig5 Традиционный китайский
big5hkscs big5-hkscs, hkscs Традиционный китайский
cp037 IBM037, IBM039 Английский
cp424 EBCDIC-CP-HE, IBM424 Еврейский
cp437 437, IBM437 Английский
cp500 EBCDIC-CP-BE, EBCDIC-CP-CH, IBM500 Западная Европа
cp737 Греческий
cp775 IBM775 Балтийские языки
cp850 850, IBM850 Западная Европа
cp852 852, IBM852 Центральная и Восточная Европа
cp855 855, IBM855 Болгарский, белорусский, македонский, русский, сербский
cp856 Еврейский
cp857 857, IBM857 Турецкий
cp860 860, IBM860 Португальский
cp861 861, CP-IS, IBM861 Исландский
cp862 862, IBM862 Еврейский
cp863 863, IBM863 Канадский
cp864 IBM864 Арабский
cp865 865, IBM865 датский, норвежский
cp866 866, IBM866 Русский
cp869 869, CP-GR, IBM869 Греческий
cp874 Тайский
cp875 Греческий
cp932 932, ms932, mskanji, ms-kanji Японский
cp949 949, ms949, uhc Корейский
cp950 950, мс 950 Традиционный китайский
cp1006 урду
cp1026 ibm1026 Турецкий
cp1140 ibm1140 Западная Европа
CP1250 окна-1250 Центральная и Восточная Европа
cp1251 окна-1251 Болгарский, белорусский, македонский, русский, сербский
cp1252 окна-1252 Западная Европа
cp1253 окна-1253 Греческий
cp1254 окна-1254 Турецкий
cp1255 окна-1255 Еврейский
cp1256 окна1256 Арабский
cp1257 окна-1257 Балтийские языки
cp1258 окна-1258 Вьетнамский
euc_jp eucjp, ujis, u-jis Японский
euc_jis_2004 jisx0213, eucjis2004 Японский
euc_jisx0213 eucjisx0213 Японский
euc_kr euckr, корейский, ksc5601, ks_c-5601, ks_c-5601-1987, ksx1001, ks_x-1001 Корейский
gb 2312 китайский, csiso58gb231280, euc-cn, euccn, eucgb2312-cn, gb2312-1980,
gb2312-80, iso-ir-58
Упрощенный китайский
gbk 936, cp936, ms936 Единый китайский
GB 18030 гб18030-2000 Единый китайский
Гц хзгб, хз-гб, хз-гб-2312 Упрощенный китайский
iso2022_jp csiso2022jp, iso2022jp, iso-2022-jp Японский
iso2022_jp_1 iso2022jp-1, iso-2022-jp-1 Японский
iso2022_jp_2 iso2022jp-2, iso-2022-jp-2 Японский, корейский, упрощенный китайский, Западная Европа, греческий
iso2022_jp_2004 iso2022jp-2004, iso-2022-jp-2004 Японский
iso2022_jp_3 iso2022jp-3, iso-2022-jp-3 Японский
iso2022_jp_ext iso2022jp-ext, iso-2022-jp-ext Японский
iso2022_kr csiso2022kr, iso2022kr, iso-2022-kr корейский
латинский_1 iso-8859-1, iso8859-1, 8859, cp819, латинский, latin1, L1 Западная Европа
iso8859_2 iso-8859-2, latin2, L2 Центральная и Восточная Европа
iso8859_3 iso-8859-3, latin3, L3 Эсперанто, мальтийский
iso8859_4 iso-8859-4, latin4, L4 Балтийские языки
iso8859_5 iso-8859-5, кириллица Болгарский, белорусский, македонский, русский, сербский
iso8859_6 iso-8859-6, арабский Арабский
iso8859_7 iso-8859-7, греческий, греческий8 Греческий
iso8859_8 iso-8859-8, иврит Еврейский
iso8859_9 iso-8859-9, latin5, L5 Турецкий
iso8859_10 iso-8859-10, latin6, L6 Скандинавские языки
iso8859_13 iso-8859-13 Балтийские языки
iso8859_14 iso-8859-14, latin8, L8 Кельтские языки
iso8859_15 iso-8859-15 Западная Европа
johab cp1361, ms1361 Корейский
koi8_r Русский
koi8_u Украинский
mac_cyrillic макцирилловый Болгарский, белорусский, македонский, русский, сербский
mac_greek макгрик Греческий
mac_iceland макисленд Исландский
mac_latin2 маклатин2, макцентралевроп Центральная и Восточная Европа
mac_roman макроман Западная Европа
mac_turkish мактуркский Турецкий
ptcp154 csptcp154, pt154, cp154, кириллица-азиатская Казахский
shift_jis csshiftjis, shiftjis, sjis, s_jis Японский
shift_jis_2004 shiftjis2004, sjis_2004, sjis2004 Японский
shift_jisx0213 shiftjisx0213, sjisx0213, s_jisx0213 Японский
utf_16 U16, utf16 все языки
utf_16_be UTF-16BE все языки (только BMP)
utf_16_le UTF-16LE все языки (только BMP)
utf_7 U7 все языки
utf_8 U8, UTF, utf8 все языки

.

Unicode HOWTO — документация Python 3.8.6

Определения

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

Unicode (https://www.unicode.org/) — это спецификация, направленная на
перечислите все символы, используемые в человеческих языках, и дайте каждому символу
собственный уникальный код. Спецификации Unicode постоянно
переработан и обновлен, чтобы добавить новые языки и символы.

Символ — наименьший возможный компонент текста. «А», «В», «С»,
и т.д., все разные персонажи. Таковы ‘’ и ‘’. Персонажи различаются
в зависимости от языка или контекста, на котором вы говорите
около.Например, для «римской цифры один» есть символ «», то есть
отдельно от заглавной буквы «I». Обычно они выглядят одинаково,
но это два разных персонажа, которые имеют разные значения.

Стандарт Unicode описывает, как символы представлены
кодовых точек . Значение кодовой точки — это целое число в диапазоне от 0 до
0x10FFFF (около 1,1 миллиона значений, из них около 110 тысяч назначены так
далеко). В стандарте и в этом документе написана кодовая точка
используя обозначение U + 265E для обозначения символа со значением
0x265e (9822 в десятичной системе).

Стандарт Unicode содержит множество таблиц, в которых перечислены символы и
их соответствующие кодовые точки:

 0061 'а'; ЛАТИНСКАЯ СТРОЧНАЯ БУКВА A
0062 'b'; ЛАТИНСКАЯ СТРОЧНАЯ БУКВА B
0063 'c'; ЛАТИНСКАЯ СТРОЧНАЯ БУКВА C
...
007B '{'; КРОНШТЕЙН ЛЕВЫЙ
...
2167 'Ⅷ'; РИМСКОЕ ЧИСЛО ВОСЕМЬ
2168 'Ⅸ'; РИМСКОЕ ЧИСЛО ДЕВЯТЬ
...
265E '♞'; ЧЕРНЫЙ ШАХМАТНЫЙ РЫЦАРЬ
265F '♟'; ЧЕРНАЯ ШАХМАТНАЯ ПАПКА
...
1F600 '😀'; УЖЕСТВЕННОЕ ЛИЦО
1F609 '😉'; Подмигивающее лицо
...
 

Строго говоря, эти определения подразумевают, что бессмысленно говорить «это
символ U + 265E ’. U + 265E — это кодовая точка, которая представляет собой
характер; в данном случае это символ «ЧЕРНЫЙ ШАХМАТНЫЙ РЫЦАРЬ»,
‘♞’. В
неформальных контекстах, это различие между кодовыми точками и символами будет
иногда забывают.

Персонаж представлен на экране или на бумаге набором графических
элементы, которые называются глифом . Глиф для прописной буквы A, например,
это два диагональных штриха и горизонтальный штрих, хотя точные детали будут
зависят от используемого шрифта.Большинству кода Python не нужно беспокоиться о
глифы; определение правильного глифа для отображения обычно является задачей графического интерфейса.
инструментарий или средство визуализации шрифтов терминала.

Кодировки

Подводя итог предыдущему разделу: строка Unicode — это последовательность
кодовые точки, которые представляют собой числа от 0 до 0x10FFFF (1,114,111
десятичный). Эта последовательность кодовых точек должна быть представлена ​​в
память как набор из кодовых единиц и кодовых единиц затем отображаются
в 8-битные байты.Правила перевода строки Unicode в
последовательность байтов называется кодировкой символов , или просто
кодировка .

Первая кодировка, о которой вы можете подумать, использует 32-битные целые числа в качестве
код, а затем с использованием представления ЦП 32-битных целых чисел.
В этом представлении строка «Python» может выглядеть так:

 П о т о н
0x50 00 00 00 79 00 00 00 74 00 00 00 68 00 00 00 6f 00 00 00 6e 00 00 00
   0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 

Это простое представление, но его использование дает ряд
проблемы.

  1. Непереносной; разные процессоры по-разному упорядочивают байты.

  2. Очень расточительно места. В большинстве текстов большинство кодовых точек
    меньше 127 или меньше 255, поэтому много места занимает 0x00
    байты. Вышеупомянутая строка занимает 24 байта по сравнению с 6 байтами, необходимыми для
    Представление ASCII. Увеличенное использование ОЗУ не имеет большого значения (настольный компьютер
    у компьютеров есть гигабайты оперативной памяти, и строки обычно не такие большие), но
    увеличение использования диска и пропускной способности сети в 4 раза — это
    невыносимо.

  3. Он несовместим с существующими функциями C, такими как strlen () , поэтому новый
    необходимо использовать семейство широких строковых функций.

Таким образом, эта кодировка используется нечасто, и люди вместо этого выбирают другую
более эффективные и удобные кодировки, например UTF-8.

UTF-8 — одна из наиболее часто используемых кодировок, а Python часто
по умолчанию использует его. UTF означает «Формат преобразования Unicode»,
а «8» означает, что при кодировании используются 8-битные значения.(Там
также являются кодировками UTF-16 и UTF-32, но реже
используется, чем UTF-8.) UTF-8 использует следующие правила:

  1. Если кодовая точка <128, она представлена ​​соответствующим байтовым значением.

  2. Если кодовая точка> = 128, она превращается в последовательность из двух, трех или
    четыре байта, где каждый байт последовательности находится в диапазоне от 128 до 255.

UTF-8 имеет несколько удобных свойств:

  1. Он может обрабатывать любую кодовую точку Unicode.

  2. Строка Unicode превращается в последовательность байтов, содержащую встроенные
    нулевые байты только там, где они представляют собой нулевой символ (U + 0000). Это означает
    что строки UTF-8 могут обрабатываться функциями C, такими как strcpy () , и отправляться
    через протоколы, которые не могут обрабатывать нулевые байты ни для чего, кроме
    маркеры конца строки.

  3. Строка текста ASCII также является допустимым текстом UTF-8.

  4. UTF-8 довольно компактен; большинство часто используемых символов могут быть
    представлен одним или двумя байтами.

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

  6. UTF-8 — это байтовая кодировка. Кодировка указывает, что каждый
    символ представлен определенной последовательностью из одного или нескольких байтов. Эта
    позволяет избежать проблем с порядком байтов, которые могут возникнуть при использовании целых чисел и слов
    кодировки, такие как UTF-16 и UTF-32, где последовательность байтов меняется в зависимости от
    на оборудовании, на котором была закодирована строка.

Список литературы

На сайте Консорциума Unicode есть таблицы символов,
глоссарий и PDF-версии спецификации Unicode. Будьте готовы к некоторым
трудное чтение. Хронология
происхождение и развитие Unicode также доступно на сайте.

На канале Youtube Computerphile Том Скотт кратко
обсуждает историю Unicode и UTF-8
(9 минут 36 секунд).

Чтобы помочь понять стандарт, Юкка Корпела написал вводную
руководство по чтению
Таблицы символов Юникода.

Еще одна хорошая вводная статья
был написан Джоэлом Спольски.
Если это введение не прояснило вам ситуацию, попробуйте
Прочтите эту альтернативную статью, прежде чем продолжить.

записи в Википедии часто бывают полезны; см., например, записи «кодировка символов» и UTF-8.

.

Работа с кодировкой UTF-8 в исходном коде Python

Переполнение стека

  1. Около
  2. Продукты

  3. Для команд
  1. Переполнение стека
    Общественные вопросы и ответы

  2. Переполнение стека для команд
    Где разработчики и технологи делятся частными знаниями с коллегами

  3. Вакансии
    Программирование и связанные с ним технические возможности карьерного роста

  4. Талант
    Нанимайте технических специалистов и создавайте свой бренд работодателя

  5. Реклама
    Обратитесь к разработчикам и технологам со всего мира

  6. О компании

.

Unicode HOWTO — документация Python 2.7.18

История кодов персонажей

В 1968 году Американский стандартный код обмена информацией, более известный
его аббревиатура ASCII была стандартизирована. Цифровые коды ASCII для различных
символов, с числовыми значениями от 0 до
127. Например, строчной букве «а» присвоен код 97.
ценность.

ASCII был стандартом, разработанным в Америке, поэтому он определял только безударные
символы. Там было «е», но не было «é» или «».Это означало, что языки
для которых требовались символы с диакритическими знаками, которые не могли быть точно представлены в ASCII.
(На самом деле отсутствующие акценты имеют значение и для английского языка, который содержит такие слова
как «наивный» и «кафе», а в некоторых публикациях есть домашние стили, требующие
варианты написания, такие как «coöperate».)

Какое-то время люди просто писали программы, в которых не было акцентов. я помню
смотрит на Apple] [программы BASIC, опубликованные во французских изданиях в
в середине 1980-х годов были такие строки:

 ПЕЧАТЬ "СРОЧКА ПУТЕШЕСТВИЯ"
ПЕЧАТЬ "ЗАПИСИ ПАРАМЕТРОВ"
 

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

В 1980-х почти все персональные компьютеры были 8-битными, а это означало, что байты могли
содержат значения от 0 до 255. Коды ASCII увеличились только до 127, поэтому некоторые
машины присвоили акцентированным символам значения от 128 до 255. Другой
однако машины имели разные коды, что приводило к проблемам при обмене файлами.
Со временем появились различные обычно используемые наборы значений для диапазона 128–255.
Некоторые из них были настоящими стандартами, определенными Международной организацией
Стандартизация, а некоторые из них были де-факто конвенций, которые были изобретены одним
компании или другой и сумел завоевать популярность.

255 символов — это немного. Например, вы не можете разместить оба акцентированных
символы, используемые в Западной Европе, и кириллица, используемая для русского языка
в диапазон 128–255, потому что таких символов больше 128.

Вы можете писать файлы, используя разные коды (все ваши русские файлы в кодировке
система под названием KOI8, все ваши французские файлы в другой системе кодирования, называемой
Latin1), но что, если вы хотите написать французский документ, который цитирует некоторые
Русский текст? В 80-е годы люди захотели решить эту проблему, и
Начались усилия по стандартизации Unicode.16 = 65 536 различных значений доступны, что делает возможным
представлять множество разных символов из разных алфавитов; начальный
Цель заключалась в том, чтобы Unicode содержал алфавиты для каждого отдельного человеческого языка.
Оказалось, что даже 16 бит недостаточно для достижения этой цели, и современные
В спецификации Unicode используется более широкий диапазон кодов: 0–1,114,111 (0x10ffff в
база-16).

Существует родственный стандарт ISO, ISO 10646. Unicode и ISO 10646 были
изначально отдельные усилия, но спецификации были объединены с 1.1
версия Unicode.

(Это обсуждение истории Unicode очень упрощено. Я не думаю, что
среднему программисту Python нужно беспокоиться об исторических деталях; консультироваться
сайт консорциума Unicode, указанный в справочной документации для получения дополнительной информации.)

Определения

символ — наименьший возможный компонент текста. «А», «В», «С»,
и т.д., все разные персонажи. Таковы ‘’ и ‘’. Персонажи
абстракции и различаются в зависимости от языка или контекста, на котором вы говорите
около.Например, обозначение Ом (Ом) обычно рисуют так же, как
заглавная буква омега (Ω) в греческом алфавите (они могут даже быть одинаковыми в
некоторые шрифты), но это два разных символа, которые имеют разные
смыслы.

Стандарт Unicode описывает, как символы представлены кодом
точки
. Кодовая точка — это целое число, обычно обозначаемое по основанию 16. В
стандартная кодовая точка записывается с использованием обозначения U + 12ca для обозначения
символ со значением 0x12ca (4810 десятичных).Стандарт Unicode содержит много
таблиц, в которых перечислены символы и соответствующие им кодовые точки:

 0061 'а'; ЛАТИНСКАЯ СТРОЧНАЯ БУКВА A
0062 'b'; ЛАТИНСКАЯ СТРОЧНАЯ БУКВА B
0063 'c'; ЛАТИНСКАЯ СТРОЧНАЯ БУКВА C
...
007B '{'; КРОНШТЕЙН ЛЕВЫЙ
 

Строго говоря, эти определения подразумевают, что бессмысленно говорить «это
символ U + 12ca ’. U + 12ca — это кодовая точка, которая представляет собой определенную
характер; в данном случае он представляет собой символ «ETHIOPIC SYLLABLE WI». В
неформальных контекстах, это различие между кодовыми точками и символами будет
иногда забывают.

Персонаж представлен на экране или на бумаге набором графических
элементы, которые называются глифом . Глиф для прописной буквы A, например,
это два диагональных штриха и горизонтальный штрих, хотя точные детали будут
зависят от используемого шрифта. Большинству кода Python не нужно беспокоиться о
глифы; определение правильного глифа для отображения обычно является задачей графического интерфейса.
инструментарий или средство визуализации шрифтов терминала.

Кодировки

Подводя итог предыдущему разделу: строка Unicode — это последовательность кода
точки — числа от 0 до 0x10ffff.Эта последовательность должна быть
представлен в виде набора байтов (то есть значений от 0 до 255) в памяти. Правила
для перевода строки Unicode в последовательность байтов называются
кодировка .

Первая кодировка, о которой вы можете подумать, — это массив 32-битных целых чисел. В этом
представление, строка «Python» будет выглядеть так:

 П о т о н
0x50 00 00 00 79 00 00 00 74 00 00 00 68 00 00 00 6f 00 00 00 6e 00 00 00
   0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 

Это простое представление, но его использование дает ряд
проблемы.

  1. Непереносной; разные процессоры по-разному упорядочивают байты.

  2. Очень расточительно места. В большинстве текстов большинство кодовых точек
    меньше 127 или меньше 255, поэтому много места занимает ноль
    байты. Вышеупомянутая строка занимает 24 байта по сравнению с 6 байтами, необходимыми для
    Представление ASCII. Увеличенное использование ОЗУ не имеет большого значения (настольный компьютер
    компьютеры имеют мегабайты оперативной памяти, и строки обычно не такие большие), но
    увеличение использования диска и пропускной способности сети в 4 раза — это
    невыносимо.

  3. Он несовместим с существующими функциями C, такими как strlen () , поэтому новый
    необходимо использовать семейство широких строковых функций.

  4. Многие интернет-стандарты определены в виде текстовых данных и не могут
    обрабатывать содержимое со встроенными нулевыми байтами.

Обычно люди не используют эту кодировку, вместо этого выбирают другую
кодировки, которые более эффективны и удобны. UTF-8, вероятно,
наиболее часто поддерживаемая кодировка; об этом будет сказано ниже.

Кодировки

не должны обрабатывать все возможные символы Юникода, и большинство
кодировки нет. Например, кодировка Python по умолчанию — «ascii».
кодирование. Правила преобразования строки Unicode в кодировку ASCII:
просто; для каждой кодовой точки:

  1. Если кодовая точка <128, каждый байт совпадает со значением кода точка.

  2. Если кодовая точка 128 или больше, строка Unicode не может быть представлена
    в этой кодировке.(Python вызывает исключение UnicodeEncodeError в этом
    чехол.)

Latin-1, также известная как ISO-8859-1, представляет собой аналогичную кодировку. Кодовые точки Unicode
0–255 идентичны значениям Latin-1, поэтому преобразование в эту кодировку просто
требует преобразования кодовых точек в байтовые значения; если кодовая точка больше 255
встречается, строка не может быть закодирована в Latin-1.

Кодировки

не обязательно должны быть простыми взаимно-однозначными сопоставлениями, такими как Latin-1. Рассмотреть возможность
EBCDIC IBM, который использовался на мэйнфреймах IBM.Буквенные значения не были в одном
блок: от «a» до «i» были значения от 129 до 137, а от «j» до «r» — 145
по 153. Если вы хотите использовать EBCDIC в качестве кодировки, вы, вероятно, использовали бы
какая-то таблица поиска для выполнения преобразования, но это в основном
внутренняя деталь.

UTF-8 — одна из наиболее часто используемых кодировок. UTF означает «Unicode
Формат преобразования », а« 8 »означает, что 8-битные числа используются в
кодирование. (Также существует кодировка UTF-16, но она используется реже, чем
UTF-8.) UTF-8 использует следующие правила:

  1. Если кодовая точка <128, она представлена ​​соответствующим значением байта.

  2. Если кодовая точка находится между 128 и 0x7ff, она преобразуется в двухбайтовые значения.
    от 128 до 255.

  3. Кодовые точки> 0x7ff преобразуются в трех- или четырехбайтовые последовательности, где каждая
    байт последовательности находится между 128 и 255.

UTF-8 имеет несколько удобных свойств:

  1. Он может обрабатывать любую кодовую точку Unicode.

  2. Строка Unicode превращается в строку байтов, не содержащую встроенного нуля
    байты. Это позволяет избежать проблем с порядком байтов и означает, что строки UTF-8 могут быть
    обрабатывается функциями C, такими как strcpy () , и отправляется через протоколы, которые
    не может обрабатывать нулевые байты.

  3. Строка текста ASCII также является допустимым текстом UTF-8.

  4. UTF-8 довольно компактен; большинство кодовых точек превращены в две
    байтов, а значения меньше 128 занимают только один байт.

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

.

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

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

2021 © Все права защищены. Карта сайта