Значения символов Latin1 не отображаются так же, как в utf8

ДЛЯ PYTHON 2.7 (я попробовал использовать encode в 3, и теперь я все запутался... хотелось бы получить совет, как воспроизвести этот тест в python 3....)

Для символа евро (€) я посмотрел, что использовала его шестнадцатеричная кодовая точка utf8, используя этот инструмент. Он сказал, что это 0x20AC.

Для Latin1 (опять же с использованием Python2 2.7) я использовал декодирование, чтобы получить его шестнадцатеричный код:

>>import unicodedata
>>p='€'
## notably x80 seems to correspond to [Windows CP1252 according to the link][2]
>>p.decode('latin-1') 
>>u'\x80'

Затем я использовал этот оператор печати для обоих из них, и вот что я получил:

для utf8:

>>> print unichr(0x20AC).encode('utf-8')
€

для латыни-1:

>>> print unichr(0x80).encode('latin-1')
€

Что, черт возьми, произошло? Почему encode возвращает «¬» для utf-8? Кроме того... кажется, что шестнадцатеричные кодовые точки Latin1 МОГУТ отличаться от их аналогов utf8 (у меня есть коллега, который считает иначе - говорит, что Latin1 в этом отношении похож на ASCII). Но наличие разных кодовых точек, кажется, говорит мне об обратном... ОДНАКО причина, по которой python 2.7 читает Windows CP1252 'x80' для меня настоящая загадка.... это стандарт для latin-1 в python 2.7??


person user14696    schedule 11.12.2013    source источник
comment
Во-первых, UTF-8 для символа евро — '\xE2\x82\xAC'. В UTF-8 '\x20\xAC' — это пробел, за которым следует недопустимый символ.   -  person abarnert    schedule 11.12.2013
comment
Вы говорите, что мне нужно ввести '\xe2\x82\xac' ?? это не имеет для меня смысла.... или python. Я получаю SyntaxError: неверный синтаксис, когда я ввожу: ››› print unichr('\xe2\x82\xac').encode('utf-8')   -  person user14696    schedule 11.12.2013
comment
Во-первых, вы не поставили кавычки. Во-вторых, unichr принимает число, а не строку, так что это все равно не сработает. Чтобы преобразовать строку байтов в unicode, вам нужно использовать метод decode и указать кодировку, из которой вы хотите преобразовать. Итак, print '\xe2\x82\xac'.decode('utf-8').encode('utf-8') даст вам... именно то, с чего вы начали, как и следовало ожидать.   -  person abarnert    schedule 11.12.2013
comment
Извините, отредактируйте. Пишет: SyntaxError: неверный синтаксис   -  person user14696    schedule 11.12.2013
comment
Тогда вы скопировали и вставили неправильно. Если я вставлю этот точный код в свой интерпретатор Python 2.7 или онлайн-интерпретатор, такой как этот, SyntaxError не будет. ; он выводит на машине с консолью UTF-8, € на машине с консолью CP-1252 и т. д.   -  person abarnert    schedule 11.12.2013
comment
Вы все еще сбиты с толку вопросом в конце? На всякий случай: CP1252 не является стандартом для Latin-1 в Python 2.7. Ваш терминал (также известный как приглашение DOS) отвечает за преобразование нажатий клавиш в байты для отправки в Python. Так как вы работаете в системе Windows с CP1252 в качестве кодовой страницы OEM, ваш терминал все кодирует как CP1252. Если вы позволите Python декодировать его как CP1252 или просто обработаете его как байты и пропустите нетронутым, все будет в порядке. Если вы попытаетесь трактовать его как латиницу-1, вы получите правильный ответ для многих символов, но неправильный для нескольких десятков, включая символ евро.   -  person abarnert    schedule 11.12.2013


Ответы (1)


У вас тут серьезные недоразумения. Если вы не читали HOWTO по Unicode для Python 2 и Python 3, вы должны начать с него.

Во-первых, UTF-8 — это кодировка Unicode в 8-битные байты. Не существует такой вещи, как кодовая точка UTF-8 0x20AC. Существует кодовая точка Unicode U+20AC, но в UTF-8 это три байта: 0xE2, 0x82, 0xAC.


И это объясняет ваше замешательство здесь:

Почему encode возвращает «¬» для utf-8?

Это не так. Он вернул строку байтов '\xE2\x82\xAC'. Затем вы printотправили это на свою консоль. Ваша консоль предположительно находится в CP-1252, поэтому она интерпретировала эти байты, как если бы они были CP-1252, что дало вам €.


Между тем, когда вы пишете это:

p='€'

Консоль не предоставляет Python Unicode, она предоставляет байты Python в CP-1252, которые Python просто хранит как байты. CP-1252 для знака евро — \x80. Итак, это то же самое, что набрать:

p='\x80'

Но в Latin-1 \x80 — это не знак евро, а невидимый управляющий символ, эквивалентный Unicode U+0080. Итак, когда вы вызываете p.decode('latin-1'), вы получаете обратно u'\x80'. Что именно вы и видите.


Причина, по которой вы не можете воспроизвести это в Python 3, заключается в том, что в Python 3 str и простые строковые литералы являются строками Unicode, а не байтовыми строками. Итак, когда вы пишете это:

p='€'

… консоль отдает Python несколько байтов, которые Python затем автоматически декодирует с набором символов, который он угадал для консоли (CP-1252), в Unicode. Итак, это эквивалентно написанию этого:

p='\u20ac'

… или это:

p=b'\x80'.decode(sys.stdin.encoding)

Кроме того, вы продолжаете говорить «шестнадцатеричные кодовые точки», имея в виду множество разных вещей, ни одна из которых не имеет никакого смысла.

Кодовая точка — это концепция Unicode. Строка unicode в Python представляет собой последовательность кодовых точек. str — это последовательность байтов, а не кодовых точек. Шестнадцатеричный — это просто способ представления числа: шестнадцатеричное число 20AC или 0x20AC — это то же самое, что и десятичное число 8364, а шестнадцатеричное число 0x80 — это то же самое, что десятичное число 128.

Эта последовательность байтов не имеет никакого внутреннего значения как текст сам по себе; его нужно сочетать с кодировкой, чтобы иметь смысл. В зависимости от кодировки некоторые кодовые точки могут вообще не представляться, а для представления других может потребоваться 2 или более байтов.


Окончательно:

Кроме того... кажется, что шестнадцатеричные кодовые точки Latin1 МОГУТ отличаться от их аналогов utf8 (у меня есть коллега, который считает иначе - говорит, что Latin1 в этом отношении похож на ASCII).

Latin-1 является надмножеством ASCII. Unicode также является надмножеством печатного подмножества Latin-1; некоторые символы Unicode до U+FF (и все печатные символы до U+7F) кодируются в UTF-8 как байт с тем же значением, что и кодовая точка, но не все. CP-1252 – это другое надмножество печатного подмножества Latin-1. Поскольку нет знака евро ни в ASCII, ни в Latin-1, вполне разумно, чтобы CP-1252 и UTF-8 представляли его по-разному.

person abarnert    schedule 11.12.2013
comment
В вашем последнем абзаце есть ошибка: Unicode, а не UTF-8, является надмножеством печатных частей Latin-1. - person jwodder; 11.12.2013
comment
Что такое «все печатные части Latin-1»? Я читал python 2 How-To дюжину раз (например, esp ... Latin-1, также известный как ISO-8859-1, является аналогичной кодировкой. Кодовые точки Unicode 0-255 идентичны Latin- 1 значения..). Я прошу прощения за неправильное использование «шестнадцатеричных кодовых точек» (да, я имею в виду кодовую точку Unicode), и это то, что меня смущает - как может кодовая точка для символа Latin-1 Euro отличаться от utf -8 символов, если utf8 является «надмножеством» Latin-1 ?? - person user14696; 11.12.2013
comment
@jwodder: Спасибо; Я переписал, чтобы было понятнее, надеюсь. - person abarnert; 11.12.2013
comment
@user14696: см. статью в Википедии. Latin-1 на самом деле не определяет 0x80-0x9F, поэтому не совсем верно, что кодовая точка Unicode U+80 идентична Latin-1 0x80, потому что не существует Latin-1 0x80. Но Unicode определяет 0x80-09F как зарезервированные управляющие символы, так что это не имеет большого значения. - person abarnert; 11.12.2013
comment
@ user14696: Между тем, даже в исходной версии последнего абзаца объяснялось, что в Latin-1 нет знака евро. Вы не используете Latin-1, вы используете CP-1252, расширение Latin-1, которое определяет (части) 0x80-0x9F для символов, которых нет в Latin-1, и которые сопоставляются повсюду в Юникоде. CP-1252 0x80 — это U+20AC, ; Latin-1 0x80 — это U+0080, зарезервированный управляющий символ. - person abarnert; 11.12.2013
comment
@abarnet: Спасибо... Кажется, я начинаю видеть здесь «свет». Я просмотрел точки Юникода, которые вы здесь упомянули (например, U+FF), и я определенно думаю, что понимаю, о чем вы говорите. Большое спасибо за уточнение! utf8-chartable.de/unicode-utf8-table.pl - person user14696; 11.12.2013