关于Unicode:Python UTF-16 WAVY DASH编码问题

Python UTF-16 WAVY DASH encoding question / issue

我今天正在做一些工作,遇到一个"看起来很有趣"的问题。 我一直在将某些字符串数据解释为utf-8,并检查了编码形式。 数据来自ldap(特别是Active Directory),是通过python-ldap获得的。 没有惊喜。

因此,我遇到了几次字节序列'\ xe3 \ x80 \ xb0',当解码为utf-8时,它是unicode码点3030(波浪破折号)。 我需要utf-16中的字符串数据,所以自然地我通过.encode('utf-16')进行了转换。 不幸的是,似乎python不喜欢这个字符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
D:\\> python
Python 2.6.4 (r264:75708, Oct 26 2009, 08:23:19) [MSC v.1500 32 bit (Intel)] on win32
Type"help","copyright","credits" or"license" for more information.
>>> u"\\u3030"
u'\\u3030'
>>> u"\\u3030".encode("utf-8")
'\\xe3\\x80\\xb0'
>>> u"\\u3030".encode("utf-16-le")
'00'
>>> u"\\u3030".encode("utf-16-be")
'00'
>>> '\\xe3\\x80\\xb0'.decode('utf-8')
u'\\u3030'
>>> '\\xe3\\x80\\xb0'.decode('utf-8').encode('utf-16')
'\\xff\\xfe00'
>>> '\\xe3\\x80\\xb0'.decode('utf-8').encode('utf-16-le').decode('utf-8')
u'00'

看来IronPython也不是粉丝:

1
2
3
4
5
6
7
8
9
D:\\ipy
IronPython 2.6 Beta 2 (2.6.0.20) on .NET 2.0.50727.3053
Type"help","copyright","credits" or"license" for more information.
>>> u"\\u3030"
u'\\u3030'
>>> u"\\u3030".encode('utf-8')
u'\\xe3\\x80\\xb0'
>>> u"\\u3030".encode('utf-16-le')
'00'

如果有人可以告诉我这里到底发生了什么,将不胜感激。


但是它可以解码:

1
2
3
4
>>> u"\\u3030".encode("utf-16-le")
'00'
>>> '00'.decode("utf-16-le")
u'\\u3030'

这是因为该字符的UTF-16编码与" 0"的ASCII码一致。您也可以用'\ x30 \ x30'表示它:

1
2
>>> '00' == '\\x30\\x30'
True

这似乎是正确的行为。以UTF-16编码时的字符u'\ u3030'与UTF-8中的'00'编码相同。看起来很奇怪,但这是正确的。

您可以看到的'\ xff \ xfe'只是字节顺序标记。

您确定要波浪线而不是其他字符吗?如果您希望使用其他字符,则可能是因为该字符在进入您的应用程序之前已经被错误编码。


您在这里被两件事弄糊涂了(也让我失望):

  • 除非您通过utf-16-be等指定使用哪个字节顺序,否则utf-16和utf-32编码使用BOM。这是倒数第二行的\ xff \ xfe。
  • '00'是字符数字零的两个。它不是空字符。无论如何,打印方式会有所不同:

    1
    2
    >>> '\\0\\0'
    '\\x00\\x00'

  • 上面的示例代码中有一个基本错误。请记住,您将Unicode编码为编码的字符串,然后从编码的字符串解码回Unicode。所以你也是:

    1
    '\\xe3\\x80\\xb0'.decode('utf-8').encode('utf-16-le').decode('utf-8')

    转换为以下步骤:

    1
    2
    3
    4
    '\\xe3\\x80\\xb0' # (some string)
    .decode('utf-8') # decode above text as UTF-8 encoded text, giving u'\\u3030'
    .encode('utf-16-le') # encode u'\\u3030' as UTF-16-LE, i.e. '00'
    .decode('utf-8') # OOPS! decode using the wrong encoding here!

    u'\ u3030'实际上在UTF-16LE中被编码为'00'(两次ASCII零),但是您不知何故认为这是一个空字节('\ 0')或类似的东西。

    请记住,如果使用一个编码并使用另一种编码进行解码,则无法到达相同的字符:

    1
    2
    3
    4
    5
    6
    >>> import unicodedata as ud
    >>> c= unichr(193)
    >>> ud.name(c)
    'LATIN CAPITAL LETTER A WITH ACUTE'
    >>> ud.name(c.encode("cp1252").decode("cp1253"))
    'GREEK CAPITAL LETTER ALPHA'

    在这段代码中,我编码为Windows-1252,然后从Windows-1253解码。在您的代码中,您编码为UTF-16LE并从UTF-8解码。