Благодаря разным частям из разных ответов, я думаю, мы можем найти объяснение.
Пытаясь напечатать строку в юникоде, u '\ xe9', Python неявно пытается кодировать эту строку, используя схему кодирования, которая в настоящее время хранится в sys.stdout.encoding.Python фактически выбирает этот параметр из среды, из которой он был инициирован.Если он не может найти правильную кодировку из среды, только тогда он возвращается к своему по умолчанию , ASCII.
Например, я использую оболочку bash, кодировка по умолчанию - UTF-8.Если я запускаю Python с него, он подхватывает и использует этот параметр:
$ python
>>> import sys
>>> print sys.stdout.encoding
UTF-8
Давайте на мгновение выйдем из оболочки Python и зададим среду bash с некоторой фиктивной кодировкой:
$ export LC_CTYPE=klingon
# we should get some error message here, just ignore it.
Затем снова запустите оболочку python и убедитесь, что она действительно возвращается к своей кодировке ascii по умолчанию.
$ python
>>> import sys
>>> print sys.stdout.encoding
ANSI_X3.4-1968
Бинго!
Если вы сейчас попытаетесь вывести какой-либо юникод-символ за пределы ascii, вы должны получить приятное сообщение об ошибке
>>> print u'\xe9'
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe9'
in position 0: ordinal not in range(128)
Позволяет выйти из Python и сбросить оболочку bash.
Теперь посмотрим, что произойдет после того, как Python выведет строки.Для этого мы сначала запустим оболочку bash в графическом терминале (я использую Gnome Terminal) и настроим терминал для декодирования вывода с помощью ISO-8859-1 или латинского-1 (графические терминалы обычно имеют опцию Установите кодировку символов в одном из выпадающих меню).Обратите внимание, что это не меняет фактическую кодировку среды оболочки , а только изменяет способ, которым сам терминал 1029 * будет декодировать выданный им результат, немного как веб-браузер.Поэтому вы можете изменить кодировку терминала независимо от среды оболочки.Затем давайте запустим Python из оболочки и убедимся, что для sys.stdout.encoding задана кодировка среды оболочки (для меня UTF-8):
$ python
>>> import sys
>>> print sys.stdout.encoding
UTF-8
>>> print '\xe9' # (1)
é
>>> print u'\xe9' # (2)
é
>>> print u'\xe9'.encode('latin-1') # (3)
é
>>>
(1) python выводит двоичную строку как есть, терминалполучает его и пытается сопоставить его значение с латинской картой символов.В латинице 1, 0xe9 или 233 возвращает символ «é», и именно поэтому терминал отображает.
(2) python пытается неявно кодировать строку Unicode с любой схемой, в настоящее время используемойустановить в sys.stdout.encoding, в данном случае это «UTF-8».После кодирования UTF-8 полученная двоичная строка будет иметь вид «\ xc3 \ xa9» (см. Дальнейшее объяснение).Терминал принимает поток как таковой и пытается декодировать 0xc3a9, используя latin-1, но latin-1 идет от 0 до 255 и, таким образом, декодирует только потоки 1 байт за раз.0xc3a9 имеет длину 2 байта, поэтому латинский декодер-1 интерпретирует его как 0xc3 (195) и 0xa9 (169), что дает 2 символа: Ã и ©.
(3) python кодирует кодовую точку unicode u '\xe9 '(233) со схемой latin-1.Оказывается, латинский-1 диапазон кодовых точек составляет 0-255 и указывает на тот же символ, что и Юникод в этом диапазоне.Следовательно, кодовые точки Unicode в этом диапазоне приведут к тому же значению при кодировании в латинице-1.Так что u '\ xe9' (233), закодированный в латинице-1, также выдаст двоичную строку '\ xe9'.Терминал получает это значение и пытается сопоставить его на карте символов латиницы-1.Как и в случае (1), он возвращает «é», и это то, что отображается.
Давайте теперь изменим настройки кодирования терминала на UTF-8 из выпадающего меню (как если бы вы изменили настройки кодирования вашего веб-браузера).Не нужно останавливать Python или перезапускать оболочку.Кодировка терминала теперь соответствует Python.Давайте попробуем напечатать снова:
>>> print '\xe9' # (4)
>>> print u'\xe9' # (5)
é
>>> print u'\xe9'.encode('latin-1') # (6)
>>>
(4) Python выводит строку двоичного как есть.Терминал пытается декодировать этот поток с помощью UTF-8.Но UTF-8 не понимает значение 0xe9 (см. Дальнейшее объяснение) и поэтому не может преобразовать его в кодовую точку Unicode.Кодовая точка не найдена, символ не напечатан.
(5) python пытается неявно кодировать строку Unicode с тем, что находится в sys.stdout.encoding. Еще "UTF-8". В результате получается двоичная строка '\ xc3 \ xa9'. Терминал принимает поток и пытается декодировать 0xc3a9 также с использованием UTF-8. Возвращает значение кода 0xe9 (233), которое на карте символов Unicode указывает на символ «é». Терминал отображает «é».
(6) python кодирует строку Юникода с помощью латинского-1, он выдает двоичную строку с тем же значением '\ xe9'. Опять же, для терминала это почти так же, как в случае (4).
Выводы:
- Python выводит не-юникодные строки в виде необработанных данных без учета кодировки по умолчанию. Терминал просто отображает их, если его текущая кодировка соответствует данным.
- Python выводит строки Unicode после их кодирования по схеме, указанной в sys.stdout.encoding.
- Python получает эту настройку из среды оболочки.
- терминал отображает вывод в соответствии со своими собственными настройками кодирования.
- кодировка терминала не зависит от оболочки.
Подробнее о юникоде, UTF-8 и латинице-1:
Юникод - это, в основном, таблица символов, в которой некоторые клавиши (кодовые точки) условно назначены для указания на некоторые символы. например условно было решено, что ключ 0xe9 (233) - это значение, указывающее на символ «é». ASCII и Unicode используют те же кодовые точки от 0 до 127, что и latin-1 и Unicode от 0 до 255. То есть 0x41 указывает на «A» в ASCII, латинский-1 и Unicode, 0xc8 указывает на «Ü» в латинский-1 и Unicode, 0xe9 указывает на 'é' в латинском-1 и Unicode.
При работе с электронными устройствами кодовые точки Unicode нуждаются в эффективном способе представления в электронном виде. Вот что такое схемы кодирования. Существуют различные схемы кодирования Unicode (utf7, UTF-8, UTF-16, UTF-32). Наиболее интуитивным и простым подходом кодирования было бы просто использовать значение кодовой точки на карте Unicode в качестве значения для его электронной формы, но в настоящее время Unicode имеет более миллиона кодовых точек, что означает, что для некоторых из них требуется 3 байта для выражены. Для эффективной работы с текстом сопоставление 1 к 1 было бы весьма непрактичным, поскольку для этого требовалось бы, чтобы все кодовые точки сохранялись в одинаковом объеме пространства с минимум 3 байтами на символ, независимо от их реальной потребности. 1063 *
Большинство схем кодирования имеют недостатки, касающиеся требований к пространству, самые экономичные не охватывают все кодовые точки Unicode, например, ascii охватывает только первые 128, а latin-1 охватывает первые 256. Другие, которые пытаются быть более полными в конечном итоге они также расточительны, поскольку требуют больше байтов, чем необходимо, даже для обычных «дешевых» символов. UTF-16, например, использует минимум 2 байта на символ, включая те, которые находятся в диапазоне ascii («B», который равен 65, все еще требует 2 байта памяти в UTF-16). UTF-32 является еще более расточительным, поскольку он хранит все символы в 4 байта.
UTF-8, похоже, удачно решил дилемму со схемой, способной хранить кодовые точки с переменным количеством байтов. В рамках своей стратегии кодирования UTF-8 объединяет кодовые точки с битами флага, которые указывают (предположительно декодерам) их требования к пространству и их границы.
UTF-8 кодирование кодовых точек Unicode в диапазоне ASCII (0-127):
0xxx xxxx (in binary)
- x показывают фактическое пространство, зарезервированное для «хранения» кодовой точки во время кодирования
- Ведущий 0 - это флаг, который указывает декодеру UTF-8, что для этой кодовой точки потребуется только 1 байт.
- при кодировании UTF-8 не изменяет значение кодовых точек в этом конкретном диапазоне (т. Е. 65, закодированное в UTF-8, также равно 65). Учитывая, что Unicode и ASCII также совместимы в одном и том же диапазоне, это делает UTF-8 и ASCII совместимым в этом диапазоне.
например. Кодовая точка Unicode для «B» - это «0x42» или 0100 0010 в двоичном формате (как мы уже говорили, в ASCII то же самое). После кодирования в UTF-8 оно становится:
0xxx xxxx <-- UTF-8 encoding for Unicode code points 0 to 127
*100 0010 <-- Unicode code point 0x42
0100 0010 <-- UTF-8 encoded (exactly the same)
UTF-8 кодирование кодовых точек Unicode выше 127 (не ascii):
110x xxxx 10xx xxxx <-- (from 128 to 2047)
1110 xxxx 10xx xxxx 10xx xxxx <-- (from 2048 to 65535)
- начальные биты '110' указывают декодеру UTF-8 начало кодовой точки, закодированной в 2 байта, тогда как '1110' указывает 3 байта, 11110 будет означать 4 байта и т. Д.
- внутренние биты флага '10' используются для обозначения начала внутреннего байта.
- снова, x отмечают пространство, в котором после кодирования сохраняется значение кодовой точки Unicode.
например. 'é' кодовая точка Unicode - 0xe9 (233).
1110 1001 <-- 0xe9
Когда UTF-8 кодирует это значение, он определяет, что значение больше 127 и меньше 2048, поэтому должно быть закодировано в 2 байта:
110x xxxx 10xx xxxx <-- UTF-8 encoding for Unicode 128-2047
***0 0011 **10 1001 <-- 0xe9
1100 0011 1010 1001 <-- 'é' after UTF-8 encoding
C 3 A 9
Кодовые точки 0xe9 Unicode после кодирования UTF-8 становятся 0xc3a9. Что именно так, как терминал получает его. Если ваш терминал настроен на декодирование строк с использованием latin-1 (одна из устаревших кодировок, не относящихся к юникоду), вы увидите Ã ©, потому что просто так получается, что 0xc3 в latin-1 указывает на Ã и 0xa9 на ©.