Ответ 1
Весь ключ к таким проблемам кодирования состоит в том, чтобы понять, что в принципе есть два разных понятия "строка" : (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. Кодирование также не обязательно уникально, поскольку некоторые символы могут быть представлены либо непосредственно, либо как комбинация (например, базового символа и акцентов).
Обратите внимание, что концепция новой строки добавляет уровень сложности, поскольку он может быть представлен различными (контроль), которые зависят от операционной системы (вот почему Python универсальный режим чтения строк в новой строке).
Теперь то, что я назвал "символом" выше, - это то, что Unicode вызывает " воспринимаемый пользователем символ". Один пользовательский воспринимаемый персонаж иногда может быть представлен в Юникоде, комбинируя части символов (базовый символ, акценты,...), найденный в разных индексах в Unicode список, который называется " кодовые точки "- эти кодовые точки можно объединить вместе, чтобы сформировать "кластер графем", Таким образом, Unicode приводит к третьей концепции строки, состоящей из последовательности кодовых точек Unicode, которая находится между байтовыми и символьными строками и которая ближе к последней. Я назову их " Unicode string" (например, в 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
type, literal form u"…"
), а массивы байтов - "строки" (str
type, где массив байтов, например, может быть построен со строковыми литералами "…"
). В Python 3 строки Unicode просто называются "строками" (str
type, literal form "…"
), а массивы байтов - "байты" (bytes
type, literal form b"…"
).
С этими несколькими ключевыми моментами вы должны понимать большинство вопросов, связанных с кодировкой!
Обычно, когда вы печатаете 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 (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.