4

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

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

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 似乎也不是粉丝:

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'

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

4

4 回答 4

2

But it decodes okay:

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

It's that the UTF-16 encoding of that character happens to coincide with the ASCII code for '0'. You could also represent it with '\x30\x30':

>>> '00' == '\x30\x30'
True
于 2010-02-15T22:10:24.823 回答
2

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

您可以看到的 '\xff\xfe' 只是一个Byte Order Mark

你确定你想要一个波浪形的破折号,而不是其他角色吗?如果您希望使用不同的字符,那可能是因为它在进入您的应用程序之前已经被错误编码。

于 2010-02-15T21:56:19.777 回答
1

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

  1. utf-16 和 utf-32 编码使用 BOM,除非您通过 utf-16-be 等指定要使用的字节顺序。这是倒数第二行中的 \xff\xfe。
  2. '00' 是两个字符数字 0。它不是空字符。无论如何,这会以不同的方式打印:

    >>> '\0\0'
    '\x00\x00'
    
于 2010-02-15T22:27:23.490 回答
0

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

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

这转化为以下步骤:

'\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')或其他东西。

请记住,如果您使用一种编码并使用另一种编码解码,则无法访问相同的字符:

>>> 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 解码。

于 2010-03-07T09:38:08.070 回答