Индексация юникода Python показывает другой символ

У меня есть строка Unicode в «узкой» сборке Python 2.7.10, содержащая символ Unicode. Я пытаюсь использовать этот символ Unicode для поиска в словаре, но когда я индексирую строку, чтобы получить последний символ Unicode, она возвращает другую строку:

>>> s = u'Python is fun \U0001f44d'
>>> s[-1]
u'\udc4d'

Почему это происходит и как мне получить '\U0001f44d' из строки?

Редактировать: unicodedata.unidata_version — это 5.2.0, а sys.maxunicode — это 65535.

Скриншот проблемы


person Tim    schedule 20.03.2019    source источник
comment
Если это настоящий MCVE, у вас очень странная сборка Python 2.7. Пожалуйста, отредактируйте в своем вопросе значения unicodedata.unidata_version и sys.maxunicode?   -  person wim    schedule 20.03.2019
comment
@wim Добавил эти правки. Это, по сути, настоящий MCVE.   -  person Tim    schedule 20.03.2019
comment
Не могу воспроизвести; ideone.com/y7jalr   -  person tripleee    schedule 20.03.2019
comment
Я так понимаю, len(u'\U0001f44d') возвращает 2 на вашем Python?   -  person wim    schedule 20.03.2019
comment
@wim Да, len(u'\U0001f44d') возвращает 2.   -  person Tim    schedule 20.03.2019
comment
stackoverflow.com/questions/35404144 /   -  person Josh Lee    schedule 20.03.2019
comment
Я могу на самом деле воспроизвести на MacOS Mojave, используя предустановленную /usr/bin/python. Возможно, в вашем вопросе следует упомянуть вашу платформу (хотя это видно на скриншоте, если вы знаете, где искать).   -  person tripleee    schedule 20.03.2019


Ответы (2)


«Узкая» сборка Python 2 использует UTF-16 для хранения строк Unicode (так называемая дырявая абстракция, поэтому кодовые точки >U+FFFF — это два суррогата UTF.Чтобы получить кодовую точку, вы должны получить как начальный, так и конечный суррогат:

Python 2.7.14 (v2.7.14:84471935ed, Sep 16 2017, 20:25:58) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> s = u'Python is fun \U0001f44d'
>>> s[-1]     # Just the trailing surrogate
u'\udc4d'
>>> s[-2:]    # leading and trailing
u'\U0001f44d'

Переключитесь на Python 3.3+, где проблема была решена, а сведения о хранении кодовых точек Unicode в строке Unicode не отображаются:

Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> s = u'Python is fun \U0001f44d'
>>> s[-1]   # code points are stored in Unicode strings.
'\U0001f44d'
person Mark Tolonen    schedule 20.03.2019
comment
Узкая сборка использует UCS-2, которая немного отличается от UTF-16. Подробнее см. en.wikipedia.org/wiki/UTF-16. - person PM 2Ring; 21.03.2019
comment
@ PM2Ring UCS-2 запрещает использование [суррогатов], но UTF-16 разрешает их использование парами. - person Josh Lee; 21.03.2019
comment
@JoshLee Это сложно. ;) Несмотря на поддержку суррогатных пар, документы Python 2.7 см. UCS-2. Также см. stackoverflow.com/q/53140775/4014959. - person PM 2Ring; 21.03.2019
comment
Java и JavaScript имеют очень похожее поведение, но требуют UTF-16, а не UCS-2 ???? - person Josh Lee; 21.03.2019
comment
Документы могут сказать что угодно, но поведение UTF16. Microsoft называет UTF-16 «Unicode». ???? - person Mark Tolonen; 21.03.2019

Похоже, ваша сборка Python 2 использует суррогаты для представления кодовых точек за пределами базовой многоязычной плоскости. См., например. Как работать с суррогатными парами в Python? немного фона.

Я бы рекомендовал как можно скорее переключиться на Python 3 для всего, что связано с обработкой строк.

person tripleee    schedule 20.03.2019