Весь ключ к таким проблемам кодирования состоит в том, чтобы понять, что в принципе существует два различных понятия «строка» : (1) строка символов и (2) строка / массив байтов.. Это различие долгое время в основном игнорировалось из-за исторической повсеместности кодировок, содержащих не более 256 символов (ASCII, Latin-1, Windows-1252, Mac OS Roman,…): эти кодировки отображают набор общих символов в числа от 0 до 255 (т.е. байты); относительно ограниченный обмен файлами до появления Интернета сделал эту ситуацию несовместимых кодировок допустимой, поскольку большинство программ могло игнорировать тот факт, что существовало несколько кодировок, пока они производили текст, который оставался в той же операционной системе: такие программы просто рассматривать текст как байты (через кодировку, используемую операционной системой). Правильный современный взгляд правильно разделяет эти две строковые концепции на основе следующих двух моментов:
Персонажи в основном не связаны с компьютером : их можно нарисовать на доске и т. Д., Например, بايثون, 中 蟒 и 🐍. «Символы» для машин также включают «инструкции по рисованию», такие как, например, пробелы, возврат каретки, инструкции по установке направления письма (для арабского языка и т. Д.), Диакритические знаки и т. Д. В стандарт Unicode включен очень большой список символов ; он охватывает большинство известных персонажей.
С другой стороны, компьютеры действительно должны каким-то образом представлять абстрактные символы: для этого они используют массивы байтов (включая числа от 0 до 255), потому что их память поступает в виде блоков байтов. Необходимый процесс преобразования символов в байты называется кодированием . Таким образом, компьютеру требуется кодировка для представления символов. Любой текст, присутствующий на вашем компьютере, кодируется (до тех пор, пока он не отображается), независимо от того, будет ли он отправлен на терминал (который ожидает символы, закодированные определенным образом) или сохранен в файле. Для того, чтобы их можно было отобразить или правильно «понять» (скажем, интерпретатором Python), потоки байтов декодируются в символы. Несколько кодировок(UTF-8, UTF-16,…) определены Unicode для его списка символов (Unicode, таким образом, определяет как список символов, так и кодировки для этих символов - все еще есть места, где можно увидеть выражение «Unicode encoding» как способ обозначить вездесущий UTF-8, но это неправильная терминология, поскольку Unicode предоставляет несколько кодировок).
Таким образом, компьютеры должны внутренне представлять символы байтами , и они делают это с помощью двух операций:
Кодировка : символы → байты
Расшифровка : байты → символы
Некоторые кодировки не могут кодировать все символы (например, ASCII), тогда как (некоторые) кодировки Unicode позволяют кодировать все символы Unicode. Кодировка также не обязательно уникальна , поскольку некоторые символы могут быть представлены либо напрямую, либо в виде комбинации (например, основного символа и акцентов).
Обратите внимание, что концепция новой строки добавляет уровень сложности , поскольку она может быть представлена различными (управляющими) символами, которые зависят от операционной системы (это причина того, что Python универсального режима чтения файла новой строки ).
То, что я назвал «символом» выше, - это то, что 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.
Однако, когда вы перенаправляете или передаете вывод своей программы по конвейеру , то, как правило, невозможно узнать, какова входная кодировка принимающей программы, и приведенный выше код возвращает некоторую кодировку по умолчанию: Нет (Python 2.7) или UTF-8 ( Python 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.