Из оболочки Python 2.6:
>>> import sys
>>> print sys.getdefaultencoding()
ascii
>>> print u'\xe9'
é
>>>
Я ожидал, что после оператора печати будет какая-то тарабарщина или ошибка, поскольку символ «é» не является частью ASCII, и я не указал кодировку. Думаю, я не понимаю, что означает кодировка ASCII по умолчанию.
РЕДАКТИРОВАТЬ
Я переместил правку в раздел " Ответы" и принял его, как было предложено.
'\xe9'
в терминале, настроенном для UTF-8, не будет печататьсяé
. Он напечатает заменяющий символ (обычно вопросительный знак), поскольку\xe9
он не является допустимой последовательностью UTF-8 (отсутствуют два байта, которые должны были следовать за этим ведущим байтом). Это, безусловно, не будет интерпретироваться как Latin-1.\xe9
на печатьé
.Ответы:
Я думаю, что благодаря фрагментам из различных ответов мы сможем составить объяснение.
Пытаясь напечатать строку юникода 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
Бинго!
Если теперь вы попытаетесь вывести какой-либо символ Unicode вне 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, также известного как latin-1 (графические терминалы обычно имеют опцию Set Character Кодировка в одном из раскрывающихся меню). Обратите внимание, что это не меняет фактическую кодировку среды оболочки , а только изменяет способ, которым сам терминал будет декодировать выданный им вывод, как это делает веб-браузер. Таким образом, вы можете изменить кодировку терминала независимо от среды оболочки. Затем запустим 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 выводит двоичную строку как есть, терминал получает ее и пытается сопоставить ее значение с картой символов latin-1. В latin-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 байта, поэтому декодер latin-1 интерпретирует его как 0xc3 (195) и 0xa9 (169), что дает 2 символа: Ã и ©.
(3) python кодирует кодовую точку unicode u '\ xe9' (233) по схеме latin-1. Оказывается, диапазон кодовых точек latin-1 составляет от 0 до 255 и указывает на тот же символ, что и Unicode в этом диапазоне. Следовательно, кодовые точки Unicode в этом диапазоне будут давать то же значение при кодировании в latin-1. Таким образом, u '\ xe9' (233), закодированный в latin-1, также даст двоичную строку '\ xe9'. Терминал получает это значение и пытается сопоставить его на карте символов latin-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 кодирует строку Unicode с помощью latin-1, это дает двоичную строку с тем же значением '\ xe9'. Опять же, для терминала это почти то же самое, что и в случае (4).
Выводы: - Python выводит строки, отличные от Unicode, как необработанные данные, без учета кодировки по умолчанию. Терминал просто отображает их, если его текущая кодировка совпадает с данными. - Python выводит строки Unicode после их кодирования с использованием схемы, указанной в sys.stdout.encoding. - Python получает эту настройку из среды оболочки. - терминал отображает вывод в соответствии со своими настройками кодировки. - кодировка терминала не зависит от оболочки.
Подробнее о Unicode, UTF-8 и latin-1:
Юникод - это в основном таблица символов, в которой некоторые клавиши (кодовые точки) были условно назначены для указания на некоторые символы. например, по соглашению было решено, что ключ 0xe9 (233) - это значение, указывающее на символ «é». ASCII и Unicode используют те же кодовые точки от 0 до 127, что и latin-1 и Unicode от 0 до 255. То есть 0x41 указывает на 'A' в ASCII, latin-1 и Unicode, 0xc8 указывает на 'Ü' в latin-1 и Unicode, 0xe9 указывает на 'é' в latin-1 и Unicode.
При работе с электронными устройствами кодовые точки Unicode нуждаются в эффективном способе электронного представления. Вот в чем суть схем кодирования. Существуют различные схемы кодирования Unicode (utf7, UTF-8, UTF-16, UTF-32). Самый интуитивно понятный и простой подход к кодированию - это просто использовать значение кодовой точки в карте Unicode в качестве значения для ее электронной формы, но Unicode в настоящее время имеет более миллиона кодовых точек, что означает, что для некоторых из них требуется 3 байта для выражено. Для эффективной работы с текстом сопоставление 1 к 1 было бы довольно непрактичным, поскольку для этого потребовалось бы, чтобы все кодовые точки хранились в точно таком же объеме пространства, как минимум 3 байта на символ, независимо от их фактической потребности.
Большинство схем кодирования имеют недостатки, касающиеся требований к пространству, самые экономичные из них не охватывают все кодовые точки 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)
например, кодовая точка 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)
например, «é» кодовая точка 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
Кодовые точки Unicode 0xe9 после кодировки UTF-8 становятся 0xc3a9. Именно так его и получает терминал. Если ваш терминал настроен на декодирование строк с использованием latin-1 (одна из устаревших кодировок, отличных от Unicode), вы увидите Ã ©, потому что так уж случилось, что 0xc3 в latin-1 указывает на Ã, а 0xa9 - на ©.
источник
Когда символы Unicode выводятся на стандартный вывод,
sys.stdout.encoding
используется. Предполагается, что символ не-Unicode находится внутриsys.stdout.encoding
и просто отправляется на терминал. В моей системе (Python 2):>>> import unicodedata as ud >>> import sys >>> sys.stdout.encoding 'cp437' >>> ud.name(u'\xe9') # U+00E9 Unicode codepoint 'LATIN SMALL LETTER E WITH ACUTE' >>> ud.name('\xe9'.decode('cp437')) 'GREEK CAPITAL LETTER THETA' >>> '\xe9'.decode('cp437') # byte E9 decoded using code page 437 is U+0398. u'\u0398' >>> ud.name(u'\u0398') 'GREEK CAPITAL LETTER THETA' >>> print u'\xe9' # Unicode is encoded to CP437 correctly é >>> print '\xe9' # Byte is just sent to terminal and assumed to be CP437. Θ
sys.getdefaultencoding()
используется только тогда, когда у Python нет другого варианта.Обратите внимание, что Python 3.6 или более поздней версии игнорирует кодировки в Windows и использует API-интерфейсы Unicode для записи Unicode в терминал. Предупреждения об ошибке UnicodeEncodeError отсутствуют, и отображается правильный символ, если шрифт его поддерживает. Даже если шрифт не поддерживает его, символы все равно можно вырезать и вставить из терминала в приложение с поддерживающим шрифтом, и это будет правильно. Обновить!
источник
Python REPL пытается выбрать, какую кодировку использовать из вашей среды. Если он находит что-то разумное, тогда все просто работает. Когда он не может понять, что происходит, он выходит из строя.
>>> print sys.stdout.encoding UTF-8
источник
TypeError: readonly attribute
2.7.2Вы уже указали кодировку, введя явно строку Unicode. Сравните результаты отказа от
u
префикса.>>> import sys >>> sys.getdefaultencoding() 'ascii' >>> '\xe9' '\xe9' >>> u'\xe9' u'\xe9' >>> print u'\xe9' é >>> print '\xe9' >>>
В этом случае
\xe9
Python принимает кодировку по умолчанию (Ascii), таким образом печатая ... что-то пустое.источник
Меня устраивает:
import sys stdin, stdout = sys.stdin, sys.stdout reload(sys) sys.stdin, sys.stdout = stdin, stdout sys.setdefaultencoding('utf-8')
источник
В соответствии с кодировкой и преобразованием строк по умолчанию / неявным Python :
print
ingunicode
, этоencode
d с<file>.encoding
.encoding
не установлен,unicode
неявно преобразуется вstr
(поскольку кодек для этого естьsys.getdefaultencoding()
, т. е.ascii
любые национальные символы вызовут aUnicodeEncodeError
)encoding
выводится из среды. Обычно он устанавливаетtty
потоки fot (из настроек локали терминала), но, скорее всего, не будет установлен для каналовprint u'\xe9'
, скорее всего, будет успешным, когда вывод направлен на терминал, и неудачным, если он перенаправлен. Решение -encode()
строка с желаемой кодировкой передprint
ing.print
входеstr
байты отправляются в поток как есть. Какие глифы показывает терминал, будет зависеть от настроек локали.источник