Тук имате сериозни недоразумения. Ако не сте прочели Unicode HOWTO за 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
е поредица от байтове, а не кодови точки. Hex е просто начин за представяне на число – шестнадесетичното число 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