2010-11-24 79 views
10

我正在处理一个似乎热衷于返回的应用程序,我相信是双重UTF-8编码的字符串。python中的双解码unicode

我发送使用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',它看起来像... unicode-string,包含使用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都使用?

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

回答

19

ret.decode()尝试含蓄与编码ret系统编码 - 在你的情况下ascii。

如果你明确地编码unicode字符串,你应该没问题。有一个内置的编码已经做了你需要的东西:

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

真的,.encode('latin1')(或CP1252)将是美好的,因为这是在服务器几乎cerainly使用。该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') 
'€€' 
0

不要使用此!使用@hop's solution

我讨厌黑客:(畏缩,只是静静地这不是我的错,这是服务器开发者的错!)。

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ß 
+0

好的问题,顺便说一下。令人讨厌的情况。我希望别人可以提出一个比chr(ord(c))更简洁的解决方案来将unicode转换为str,逐字符... – 2010-11-24 13:30:28

+0

'f(char)用于字符串中的字符'用于编码的呼叫。 – hop 2010-11-24 13:33:36

1

你想要什么就是Unicode代码点X被编码为相同字节值X的代码点内0-255编码您在Latin-1编码有这样的:

def double_decode(bstr): 
    return bstr.decode("utf-8").encode("latin-1").decode("utf-8")