2010-11-24 77 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")