Python中的Unicode双重解码
我正在处理一个应用程序,它似乎总是返回我认为是双重UTF-8编码的字符串。
我发送的字符串是 u'XüYß'
,用UTF-8编码后变成了 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'
,这看起来像是一个... unicode字符串,里面包含了用UTF-8编码的原始字符串。
但是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 个回答
别用这个! 用@hop的解决方案。
这是我一个很糟糕的解决办法:(让人 cringe!但要低调。这不是我的错,是服务器开发者的错)
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ß
你想要的是一种编码方式,其中Unicode的代码点X会被编码成相同的字节值X。对于0到255之间的代码点,拉丁-1编码就满足这个要求:
def double_decode(bstr):
return bstr.decode("utf-8").encode("latin-1").decode("utf-8")
ret.decode()
这个操作会试着用系统默认的编码方式来处理 ret
,在你的情况下是 ascii 编码。
如果你明确地对这个 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')
'€€'