不同的Unicode字符串编码相同

3
我有两个 Unicode 字符串,即使归一化后也不同。但是,当它们被编码为 UTF-8 时,它们是相同的。我想了解如何(也许为什么)它们不同。

它们都是实体的标识符,查找失败是因为标识符错误,然而作为字节字符串它们是相同的,所以我想了解我们如何得到不同的 Unicode 字符串(它们已由不同的子系统处理),以及我如何知道它们实际上是相同的。

"\u8a92\u6089\u5bfd\u4267\ucdb7\u5727\u4039\U0002ae18\U00025314\u30c3"

和:

"\u8a92\u6089\u5bfd\u4267\ucdb7\u5727\u4039\ud86b\ude18\ud854\udf14\u30c3"

问题似乎并非在归一化上。我意识到这个问题的元素是无法回答的,但我会感激任何线索!

>>> a = u"\u8a92\u6089\u5bfd\u4267\ucdb7\u5727\u4039\ud86b\ude18\ud854\udf14\u30c3"
>>> b = u"\u8a92\u6089\u5bfd\u4267\ucdb7\u5727\u4039\U0002ae18\U00025314\u30c3"
>>> a == b
False
>>> import unicodedata
>>> unicodedata.normalize('NFKD', a) == unicodedata.normalize('NFKD', b)
False
>>> a.encode('UTF-8') == b.encode('UTF-8')
True

6
U+D86B等字符是代理字符,即UTF16编码中用于表示U+FFFF以上实际字符的(虚假)码位。因此,字符串a没有被正确解码。正如@snakecharmerb所指出的,最近版本的Python将认为该字符串已经损坏;但是Python 2.7似乎可以允许这种情况发生。 - lenz
1个回答

0

我不确定你是如何得到你的结果的。在Python 2.7中,你的ab Unicode字符串是相同的:

    >>> a = u"\u8a92\u6089\u5bfd\u4267\ucdb7\u5727\u4039\ud86b\ude18\ud854\udf14\u30c3"
    >>> b = u"\u8a92\u6089\u5bfd\u4267\ucdb7\u5727\u4039\U0002ae18\U00025314\u30c3"
    >>> a == b
    True
    >>> a
    u'\u8a92\u6089\u5bfd\u4267\ucdb7\u5727\u4039\U0002ae18\U00025314\u30c3'
    >>> b
    u'\u8a92\u6089\u5bfd\u4267\ucdb7\u5727\u4039\U0002ae18\U00025314\u30c3'

Python 2.7内部使用UTF-16LE存储Unicode字符串。 U+FFFF以上的码点使用代理项(两个16位代码单元)进行存储。请注意,尽管以不同方式输入,但它们显示相同。这两个字符串被解析并以相同的方式存储在内部。另一个例子:

>>> s = u'\U00025314'
>>> len(s)
2
>>> hex(ord(s[0]))
'0xd854'
>>> hex(ord(s[1]))
'0xdf14'
>>> s = u'\ud854\udf14'
>>> s
u'\U00025314'

在Python 3.3+中,不稳定的抽象暴露了Unicode在内部存储的实现细节已被修复:

>>> # python 3.3+
>>> s = '\U00025314'
>>> len(s)
1
>>> hex(ord(s[0]))
>>> hex(ord(s[0]))
'0x25314'
>>> s.encode('utf8')
b'\xf0\xa5\x8c\x94'

>>> s = '\ud854\udf14'
>>> s
'\ud854\udf14'
>>> s.encode('utf8')
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
UnicodeEncodeError: 'utf-8' codec can't encode characters in position 0-1: surrogates not allowed

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接