Python 2.7: Странное поведение Юникода - PullRequest
0 голосов
/ 04 ноября 2018

Я испытываю следующее поведение в Python 2.7:

>>> a1 = u'\U0001f04f'  #1
>>> a2 = u'\ud83c\udc4f'  #2
>>> a1 == a2  #3
False
>>> a1.encode('utf8') == a2.encode('utf8')  #4
True
>>> a1.encode('utf8').decode('utf8') == a2.encode('utf8').decode('utf8')  #5
True
>>> u'\ud83c\udc4f'.encode('utf8') #6
'\xf0\x9f\x81\x8f'
>>> u'\ud83c'.encode('utf8')  #7
'\xed\xa0\xbc'
>>> u'\udc4f'.encode('utf8')  #8
'\xed\xb1\x8f'
>>> '\xd8\x3c\xdc\x4f'.decode('utf_16_be')  #9
u'\U0001f04f'

Чем объясняется такое поведение? Более конкретно:

  1. Я бы ожидал, что две строки будут равны, если утверждение # 5 истинно, а # 3 доказывает обратное.
  2. Кодирование обеих кодовых точек вместе, как в операторе № 6, дает результаты, отличающиеся от того, когда кодируются один за другим в # 7 и # 8. Похоже, две кодовые точки рассматриваются как одна 4-байтовая кодовая точка. Но что, если я действительно хочу, чтобы они рассматривались как две разные кодовые точки?
  3. Как вы можете видеть из # 9, числа в a2 на самом деле a1 кодируются с использованием UTF-16-BE, но хотя они были определены как кодовые точки Unicode, используя \u внутри строки Unicode (!), Python еще может как-то добраться до равенства в # 5. Как это могло быть возможно?

Ничто здесь не имеет смысла! Что происходит?

1 Ответ

0 голосов
/ 04 ноября 2018

Python 2 нарушает стандарт Unicode, позволяя вам кодировать кодовые точки в диапазоне от U + D800 до U + DFFF, по крайней мере, в сборке UCS4. От Википедия :

Стандарт Unicode постоянно резервирует эти значения кодовых точек для кодирования UTF-16 суррогатов высокого и низкого уровня, и им никогда не будет назначен символ, поэтому не должно быть оснований для их кодирования. Официальный стандарт Unicode гласит, что никакие формы UTF, включая UTF-16, не могут кодировать эти кодовые точки.

Официальный стандарт UTF-8 не имеет кодировки для кодовых точек суррогатной пары UTF-16, поэтому Python 3 вызывает исключение при попытке:

>>> '\ud83c\udc4f'.encode('utf8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'utf-8' codec can't encode characters in position 0-1: surrogates not allowed

Но поддержка Unicode в Python 2 немного более рудиментарна, и наблюдаемое вами поведение варьируется в зависимости от конкретного варианта сборки UCS2 / UCS4 ; в сборке UCS2 ваши переменные равны :

>>> import sys
>>> sys.maxunicode
65535
>>> a1 = u'\U0001f04f'
>>> a2 = u'\ud83c\udc4f'
>>> a1 == a2
True

, поскольку в такой сборке все кодовые точки, отличные от BMP, кодируются как суррогатные пары UTF-16 (расширение по стандарту UCS2).

Таким образом, в сборке UCS2 нет разницы между вашими двумя значениями, и выбор кодирования в полную кодовую точку, отличную от BMP, полностью допустим , если вы предполагаете, что хотите кодировать U + 1F04F и другие такие кодовые точки. Сборка UCS4 просто соответствует этому поведению.

...