Python中的双重Unicode解码

16

我正在处理一个应用程序,它似乎喜欢返回我认为是双重UTF-8编码的字符串。

我发送了使用UTF-8编码的字符串u'XüYß',因此变成了X\u00fcY\u00df(等同于X\xc3\xbcY\xc3\x9f)。

服务器应该简单地回显我发送的内容,但返回以下内容:X\xc3\x83\xc2\xbcY\xc3\x83\xc2\x9f(应该是X\xc3\xbcY\xc3\x9f)。如果我使用str.decode('utf-8')解码它,就会变成u'X\xc3\xbcY\xc3\x9f',看起来像是一个包含使用UTF-8编码的原始字符串的Unicode字符串。

但Python不会让我在重新编码Unicode字符串之前对其进行解码-这种尝试失败了,原因我不明白:

>>> ret = 'X\xc3\x83\xc2\xbcY\xc3\x83\xc2\x9f'.decode('utf-8')
>>> ret
u'X\xc3\xbcY\xc3\x9f'
>>> ret.decode('utf-8')
# Throws UnicodeEncodeError: 'ascii' codec can't encode ...

我该如何说服Python重新解码字符串?是否有任何(实用的)方法可以调试字符串中实际包含的内容,而不需要通过所有隐式转换print使用的方式传递它?

(是的,我已向服务器端的开发人员报告了这种行为。)

4个回答

27

ret.decode()尝试使用系统编码(在您的情况下为ascii)隐式地对ret进行编码。

如果您显式地对Unicode字符串进行编码,那么就可以解决问题。有一个内置的编码可以满足您的需求:

>>> 'X\xc3\xbcY\xc3\x9f'.encode('raw_unicode_escape').decode('utf-8')
'XüYß'

实际上,.encode('latin1')(或cp1252)是可以的,因为服务器几乎肯定在使用这种编码。 raw_unicode_escape 编解码器将只会给你一些可识别的东西而不是引发异常:

>>> '€\xe2\x82\xac'.encode('raw_unicode_escape').decode('utf8')
'\\u20ac€'

>>> '€\xe2\x82\xac'.encode('latin1').decode('utf8')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'latin-1' codec can't encode character '\u20ac' in position 0: ordinal not in range(256)

如果你遇到这种混合数据,你可以再次使用编解码器来进行规范化:

>>> '€\xe2\x82\xac'.encode('raw_unicode_escape').decode('utf8')
'\\u20ac€'

>>> '\\u20ac€'.encode('raw_unicode_escape')
b'\\u20ac\\u20ac'
>>> '\\u20ac€'.encode('raw_unicode_escape').decode('raw_unicode_escape')
'€€'

呼,不需要使用我的可怕的东西。 - Chris Morgan
不知道怎么做的,但它起作用了! - Smit Johnth

3
您需要的是一种编码,其中Unicode代码点X被编码为相同的字节值X。对于0-255范围内的代码点,您可以使用Latin-1编码来实现此目的:
def double_decode(bstr):
    return bstr.decode("utf-8").encode("latin-1").decode("utf-8")

0

0

不要使用这个! 使用@hop的解决方案

我的恶劣黑客方式:(尴尬!但是请保持安静。这不是我的错,而是服务器开发人员的错)

def double_decode_unicode(s, encoding='utf-8'):
    return ''.join(chr(ord(c)) for c in s.decode(encoding)).decode(encoding)

然后,

>>> double_decode_unicode('X\xc3\x83\xc2\xbcY\xc3\x83\xc2\x9f')
u'X\xfcY\xdf'
>>> print _
XüYß

顺便说一句,这是个好问题。这是一个棘手的情况。我希望有人能想出比chr(ord(c))更简洁的解决方案,逐个字符地将Unicode转换为字符串... - Chris Morgan
f(char) for char in string 呼吁进行编码。 - user3850
通过某些函数按顺序转换字符串中的每个字符是编码和解码的定义,就是这样。 - user3850
@hop:当然,但作为解决方案,这看起来很可怕。您的.encode('raw_unicode_escape')更清晰(完全不考虑您的解决方案中unicode->str步骤比我的快六倍以上)。 - Chris Morgan

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