Завдяки шматочкам і фрагментам з різних відповідей, я думаю, що ми можемо скласти пояснення.
Намагаючись надрукувати рядок unicode, 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 aka latin-1 (графічні термінали зазвичай мають можливість встановлення символу Кодування в одному зі спадних меню). Зауважте, що це не змінює кодування фактичного середовища оболонки , воно лише змінює спосіб, яким сам термінал розшифрує отриманий дані, як і веб-браузер. Тому ви можете змінити кодування терміналу незалежно від середовища оболонки. Далі тоді запустимо 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. У латинській-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 кодує код коду унікоду u '\ xe9' (233) зі схемою lat-1. Виходить діапазон кодових точок латин-1 - 0-255 і вказує на такий самий символ, що і Unicode в цьому діапазоні. Отже, кодові точки 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 кодує рядок unicode з latin-1, він отримує двійкову рядок з тим же значенням \ \ xe9 '. Знову ж таки, для терміналу це майже те саме, що і у випадку (4).
Висновки: - Python видає рядки без унікоду як вихідні дані, не враховуючи кодування за замовчуванням. Термінал просто відбувається, щоб відобразити їх, якщо його поточне кодування відповідає даним. - Python виводить рядки Unicode після їх кодування за схемою, визначеною в sys.stdout.encoding. - Python отримує цей параметр із середовища оболонки. - термінал відображає вихід відповідно до власних налаштувань кодування. - кодування терміналу не залежить від оболонки.
Детальніше про unicode, UTF-8 та latin-1:
Unicode - це в основному таблиця символів, де умовно призначені деякі клавіші (кодові точки), які вказують на деякі символи. наприклад, за домовленістю було вирішено, що ключ 0xe9 (233) - це значення, що вказує на символ 'é'. ASCII і Unicode використовують однакові кодові точки від 0 до 127, як і латині-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 байти на символ, незалежно від їх реальної потреби.
Більшість схем кодування мають недоліки щодо простору, більшість економічних не охоплюють усіх кодів коду unicode, наприклад, ascii охоплює лише перші 128, тоді як латинська-1 охоплює перші 256. Інші, які намагаються бути більш всебічними, також закінчуються будучи марними, оскільки їм потрібно більше байтів, ніж потрібно, навіть для звичайних "дешевих" символів. Наприклад, UTF-16 використовує як мінімум 2 байти на символ, у тому числі ті, що знаходяться в діапазоні ascii ("B", який дорівнює 65, все ще потребує 2 байти зберігання в UTF-16). UTF-32 ще більш марнотратний, оскільки він зберігає всі символи в 4 байти.
UTF-8 спритно вирішив дилему, зі схемою, здатною зберігати кодові точки із змінною кількістю байтових пробілів. Як частина стратегії кодування, UTF-8 зашнуровує кодові точки з бітами прапора, які вказують (імовірно на декодери) їхні вимоги до простору та їх межі.
Кодування UTF-8 унікодових точок коду в діапазоні 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 (non-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" використовуються для сигналізації початку внутрішнього байта.
- знову ж таки, х позначає простір, де зберігається значення кодової точки 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 в латинській-1 вказує на Ã і 0xa9 на ©.