Стойностите на знаците Latin1 не се показват същите като в utf8

ЗА PYTHON 2.7 (Направих снимка на използването на encode в 3 и сега съм напълно объркан... ще се радвам на съвет как да репликирам този тест в python 3....)

За символа евро (€) потърсих каква е неговата кодова точка utf8 Hex, използвайки този инструмент. Каза, че е 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 за символа Euro е '\xE2\x82\xAC'. В UTF-8 '\x20\xAC' е интервал, последван от незаконен знак.   -  person abarnert    schedule 11.12.2013
comment
Искаш да кажеш, че трябва да въведа '\xe2\x82\xac'?? това няма смисъл за мен.... или питон. Получавам 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 или просто да го третирате като байтове и да го прехвърлите недокоснат, всичко е наред. Ако се опитате да го третирате като Latin-1, ще го направите правилно за много знаци, но грешно за няколко десетки, включително символа за евро.   -  person abarnert    schedule 11.12.2013


Отговори (1)


Тук имате сериозни недоразумения. Ако не сте прочели 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
comment
Има грешка в последния ви абзац: Unicode, а не UTF-8, е надмножество на печатаемите части на Latin-1. - person jwodder; 11.12.2013
comment
Какво представляват „всички печатаеми части на Latin-1“? Прочетох python 2 How-To дузина пъти (т.е. латински-1, известен също като ISO-8859-1, е подобно кодиране. Unicode кодови точки 0-255 са идентични с латински- 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, защото няма латински-1 0x80. Но Unicode дефинира 0x80-09F като запазени контролни знаци, така че това не е голяма работа. - person abarnert; 11.12.2013
comment
@user14696: Междувременно дори оригиналната версия на последния параграф обясняваше, че няма знак за евро в Latin-1. Вие не използвате Latin-1, вие използвате CP-1252, разширение Latin-1, което дефинира (части от) 0x80-0x9F за знаци, които не съществуват в Latin-1 и които се препоставят навсякъде в Unicode. CP-1252 0x80 е U+20AC, ; Latin-1 0x80 е U+0080, запазен контролен знак. - person abarnert; 11.12.2013
comment
@abarnet: Благодаря...мисля, че започвам да виждам „светлината“ тук. Прегледах точките на Unicode, които споменахте тук (т.е. U+FF) и определено мисля, че виждам за какво говорите. Благодаря много за разяснението! utf8-chartable.de/unicode-utf8-table.pl - person user14696; 11.12.2013