У вас тут серьезные недоразумения. Если вы не читали 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
'\xE2\x82\xAC'
. В UTF-8'\x20\xAC'
— это пробел, за которым следует недопустимый символ. - person abarnert   schedule 11.12.2013unichr
принимает число, а не строку, так что это все равно не сработает. Чтобы преобразовать строку байтов вunicode
, вам нужно использовать методdecode
и указать кодировку, из которой вы хотите преобразовать. Итак,print '\xe2\x82\xac'.decode('utf-8').encode('utf-8')
даст вам... именно то, с чего вы начали, как и следовало ожидать. - person abarnert   schedule 11.12.2013SyntaxError
не будет. ; он выводит€
на машине с консолью UTF-8,€
на машине с консолью CP-1252 и т. д. - person abarnert   schedule 11.12.2013