Весь ключ до таких проблем кодування полягає в тому, щоб зрозуміти, що в принципі існує два різних поняття "рядок" : (1) рядок символів і (2) рядок / масив байтів. Ця відмінність тривалий час ігнорується в основному через історичну повсюдність кодувань, що мають не більше 256 символів (ASCII, Latin-1, Windows-1252, Mac OS Roman,…): ці кодування відображають набір загальних символів для числа між 0 і 255 (тобто байти); відносно обмежений обмін файлами до появи Інтернету зробив цю ситуацію несумісних кодувань допустимою, оскільки більшість програм могли ігнорувати факт існування декількох кодувань, доки вони створювали текст, який залишався в одній операційній системі: такі програми просто трактувати текст як байти (через кодування, що використовується операційною системою). Правильний, сучасний погляд належним чином розділяє ці два рядкові поняття, виходячи з наступних двох моментів:
Персонажі в основному не пов'язані з комп'ютерами : їх можна намалювати на крейдовій дошці тощо, як, наприклад, بايثون, 中 蟒 та 🐍. "Символи" для машин також включають "інструкції з малювання", наприклад, пробіли, повернення каретки, інструкції щодо встановлення напрямку написання (для арабської мови тощо), наголоси тощо. У стандарт Unicode входить дуже великий список символів ; він охоплює більшість відомих персонажів.
З іншого боку, комп'ютерам потрібно певним чином представляти абстрактні символи: для цього вони використовують масиви байтів (цифри від 0 до 255 включені), оскільки їх пам'ять надходить у шматки байтів. Необхідний процес, який перетворює символи в байти, називається кодуванням . Таким чином, комп'ютер вимагає кодування для представлення символів. Будь-який текст, присутній на вашому комп’ютері, кодується (поки він не відображається), будь то надсилання до терміналу (який очікує, що символи закодовані певним чином), або збережений у файлі. Для того, щоб їх відобразили або правильно "зрозуміли" (скажімо, інтерпретатор Python), потоки байтів декодуються в символи. Кілька кодувань(UTF-8, UTF-16, ...) визначаються Unicode для його списку символів (Unicode таким чином визначає як список символів, так і кодування для цих символів - все ще є місця, де видно вираз "Кодування Unicode" як спосіб посилання на всюдисущий UTF-8, але це неправильна термінологія, оскільки Unicode надає кілька кодувань).
Підсумовуючи це, комп'ютери повинні внутрішньо представляти символи з байтами , і це роблять через дві операції:
Кодування : символи → байти
Розшифровка : байти → символи
Деякі кодування не можуть кодувати всі символи (наприклад, ASCII), тоді як (деякі) кодування Unicode дозволяють кодувати всі символи Unicode. Кодування також не обов'язково унікальне , оскільки деякі символи можуть бути представлені як безпосередньо, так і як комбінація (наприклад, основний символ та наголоси).
Зауважте, що концепція newline додає шар ускладнень , оскільки він може бути представлений різними (контрольними) символами, які залежать від операційної системи (це є причиною універсального режиму зчитування файлів нового рядка Python ).
Тепер те, що я назвав "персонажем" вище, - це те, що Unicode називає " сприйнятим користувачем символом ". Один символ, сприйнятий користувачем, іноді може бути представлений у Unicode, поєднуючи символьні частини (базовий символ, акценти, ...), знайдені в різних індексах у списку Unicode, які називаються " кодовими точками " - ці точки коду можуть бути об'єднані разом для формування "кластер графеми". Таким чином, Unicode призводить до третього поняття рядка, складеного з послідовності точок коду Unicode, яка розташована між байтовими і символьними рядками, і яка ближче до останньої. Я буду називати їх " рядками Unicode " (як у Python 2).
Хоча Python може друкувати рядки (сприймаються користувачем) символів, небайтові рядки Python - це по суті послідовності точок коду Unicode , а не символи, сприйняті користувачем. Значення точки коду - це ті, які використовуються в синтаксисі рядків Python \u
і \U
Unicode. Їх не слід плутати з кодуванням символу (і не повинні мати з ним ніяких відносин: Точки коду Unicode можна кодувати різними способами).
Це має важливий наслідок: довжина рядка Python (Unicode) - це його кількість кодових точок, що не завжди є його кількістю сприйнятих користувачем символів : таким чином s = "\u1100\u1161\u11a8"; print(s, "len", len(s))
(Python 3) дає, 각 len 3
незважаючи на s
наявність єдиного сприйманого користувачем (корейською) символу (тому що він представлений трьома кодовими точками - навіть якщо цього не потрібно, як print("\uac01")
показано). Однак у багатьох практичних обставинах довжина рядка - це його кількість сприйнятих користувачем символів, тому що багато символів, як правило, зберігаються Python як єдиний код коду Unicode.
У Python 2 рядки Unicode називаються ... "рядками Unicode" ( unicode
тип, буквальна форма u"…"
), а масиви байтів - "рядками" ( str
тип, де масив байтів, наприклад, може бути сконструйований за допомогою рядкових літералів "…"
). У Python 3 рядки Unicode просто називаються "рядками" ( str
тип, буквальна форма "…"
), а масиви байтів - "байтами" ( bytes
тип, буквальна форма b"…"
). Як наслідок, щось подібне "🐍"[0]
дає різний результат у Python 2 ( '\xf0'
, байт) та Python 3 ( "🐍"
, перший і єдиний символ).
За допомогою цих кількох ключових моментів ви зможете зрозуміти більшість питань, що стосуються кодування!
Зазвичай під час друку u"…"
на терміналі ви не повинні отримувати сміття: Python знає кодування вашого терміналу. Насправді ви можете перевірити, що очікує кодування терміналу:
% python
Python 2.7.6 (default, Nov 15 2013, 15:20:37)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> print sys.stdout.encoding
UTF-8
Якщо ваші вхідні символи можуть бути закодовані кодуванням терміналу, Python зробить це і відправить відповідні байти на ваш термінал, не нарікаючи. Потім термінал зробить все можливе, щоб відобразити символи після розшифровки вхідних байтів (в гіршому випадку шрифт терміналу не містить деяких символів і замість цього надрукує якийсь пробіл).
Якщо ваші символи введення не можуть бути кодовані кодуванням терміналу, це означає, що термінал не налаштований для відображення цих символів. Python поскаржиться (у Python з UnicodeEncodeError
тим, що рядок символів не може бути закодована таким чином, що відповідає вашому терміналу). Єдине можливе рішення - використовувати термінал, який може відображати символи (або налаштовуючи термінал так, щоб він приймав кодування, яке може представляти ваші символи, або за допомогою іншої програми терміналу). Це важливо, коли ви поширюєте програми, які можна використовувати в різних середовищах: повідомлення, які ви друкуєте, повинні бути представленими в терміналі користувача. Тому іноді краще дотримуватися рядків, які містять лише символи ASCII.
Однак, коли ви перенаправляєте або передаєте висновок своєї програми, тоді, як правило, неможливо дізнатися, що таке кодування входу приймаючої програми, і вищевказаний код повертає деяке кодування за замовчуванням: None (Python 2.7) або UTF-8 ( Пітон 3):
% python2.7 -c "import sys; print sys.stdout.encoding" | cat
None
% python3.4 -c "import sys; print(sys.stdout.encoding)" | cat
UTF-8
Однак кодування stdin, stdout та stderr може бути встановлено за допомогою PYTHONIOENCODING
змінної середовища, якщо потрібно:
% PYTHONIOENCODING=UTF-8 python2.7 -c "import sys; print sys.stdout.encoding" | cat
UTF-8
Якщо друк на терміналі не забезпечує очікування, ви можете перевірити правильність кодування UTF-8; наприклад, ваш перший символ ( \u001A
) не надрукований, якщо я не помиляюся .
На веб-сайті http://wiki.python.org/moin/PrintFails ви можете знайти таке рішення, як наступне, для Python 2.x:
import codecs
import locale
import sys
# Wrap sys.stdout into a StreamWriter to allow writing unicode.
sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout)
uni = u"\u001A\u0BC3\u1451\U0001D10C"
print uni
Для Python 3, ви можете перевірити одне з питань, заданих раніше на StackOverflow.