Я запускаю этот фрагмент дважды, в терминале Ubuntu (кодировка установлена на utf-8), один раз с, ./test.py
а затем с помощью ./test.py >out.txt
:
uni = u"\u001A\u0BC3\u1451\U0001D10C"
print uni
Без перенаправления выводит мусор. При перенаправлении я получаю UnicodeDecodeError. Может ли кто-нибудь объяснить, почему я получаю ошибку только во втором случае, а еще лучше дать подробное объяснение того, что происходит за кулисами в обоих случаях?
Ответы:
Весь ключ к таким проблемам кодирования состоит в том, чтобы понять, что в принципе существует два различных понятия «строка» : (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 будет жаловаться (в Python с
UnicodeEncodeError
поскольку символьная строка не может быть закодирована способом, который подходит вашему терминалу). Единственное возможное решение - использовать терминал, который может отображать символы (либо настроив терминал так, чтобы он принимал кодировку, которая может представлять ваши символы, либо используя другую программу терминала). Это важно при распространении программ, которые можно использовать в различных средах: сообщения, которые вы распечатываете, должны быть представлены в пользовательском терминале. Поэтому иногда лучше придерживаться строк, содержащих только символы ASCII.Однако, когда вы перенаправляете или передаете вывод своей программы по конвейеру , то, как правило, невозможно узнать, какова входная кодировка принимающей программы, и приведенный выше код возвращает некоторую кодировку по умолчанию: Нет (Python 2.7) или UTF-8 ( Python 3):
Однако при необходимости кодировку stdin, stdout и stderr можно установить с помощью
PYTHONIOENCODING
переменной среды:Если печать на терминал не дает ожидаемого результата, вы можете проверить правильность кодировки UTF-8, введенной вами вручную; например, ваш первый символ (
\u001A
) не печатается, если я не ошибаюсь .На http://wiki.python.org/moin/PrintFails вы можете найти следующее решение для Python 2.x:
Для Python 3 вы можете проверить один из вопросов, заданных ранее на StackOverflow.
источник
Python всегда кодирует строки Unicode при записи в терминал, файл, канал и т. Д. При записи в терминал Python обычно может определить кодировку терминала и правильно ее использовать. При записи в файл или канал Python по умолчанию использует кодировку ascii, если явно не указано иное. Python можно указать, что делать при передаче вывода через
PYTHONIOENCODING
переменную среды. Оболочка может установить эту переменную перед перенаправлением вывода Python в файл или канал, чтобы была известна правильная кодировка.В вашем случае вы напечатали 4 необычных символа, которые ваш терминал не поддерживает в своем шрифте. Вот несколько примеров, помогающих объяснить поведение, с символами, которые фактически поддерживаются моим терминалом (который использует cp437, а не UTF-8).
Пример 1
Обратите внимание, что
#coding
комментарий указывает кодировку, в которой исходный файл . Я выбрал utf8, чтобы поддерживать символы в исходном коде, которые не поддерживает мой терминал. Кодировка перенаправлена на stderr, чтобы ее можно было увидеть при перенаправлении в файл.Вывод (запускается прямо с терминала)
Python правильно определил кодировку терминала.
Вывод (перенаправлен в файл)
Python не смог определить кодировку (None), поэтому по умолчанию использовал ascii. ASCII поддерживает преобразование только первых 128 символов Unicode.
Вывод (перенаправлен в файл, PYTHONIOENCODING = cp437)
и мой выходной файл был правильным:
Пример 2
Теперь я добавлю в источник символ, который не поддерживается моим терминалом:
Вывод (запускается прямо с терминала)
Мой терминал не понял этого последнего китайского символа.
Вывод (запускать напрямую, PYTHONIOENCODING = 437: заменить)
Обработчики ошибок можно указать с помощью кодировки. В этом случае неизвестные символы были заменены на
?
.ignore
иxmlcharrefreplace
еще несколько вариантов. При использовании UTF8 (который поддерживает кодирование всех символов Unicode) замены никогда не будут производиться, но шрифт, используемый для отображения символов, по-прежнему должен их поддерживать.источник
PYTHONIOENCODING
. Выполнениеprint string.encode("UTF-8")
рекомендаций @Ismail сработало для меня.chcp
кодовая страница их не поддерживает. Чтобы избежать этогоUnicodeEncodeError: 'charmap'
, вы можете установитьwin-unicode-console
package.PYTHONIOENCODING=utf-8
решает проблему.Кодировать во время печати
Это связано с тем, что, когда вы запускаете скрипт вручную, python кодирует его перед выводом на терминал, когда вы его передаете, python не кодирует его сам, поэтому вам нужно кодировать вручную при выполнении ввода-вывода.
источник
win-unicode-console
(Windows), или примите параметр командной строки (если необходимо).