如何使用Python解码UTF-8字符串?

3

我有一个像这样的unicode:

\xE5\xB1\xB1\xE4\xB8\x9C \xE6\x97\xA5\xE7\x85\xA7

我知道这是一个由utf-8编码的bytes字符串表示。

请注意,字符串\xE5\xB1\xB1\xE4\xB8\x9C \xE6\x97\xA5\xE7\x85\xA7本身是<type 'unicode'>

如何将其解码为真正的字符串山东 日照

1个回答

11

如果您打印了unicode字符串的repr()输出,那么您似乎遇到了Mojibake,即使用了错误编码解码的字节数据。

首先将其重新编码为字节,然后使用正确的编解码器进行解码。这可能只需要将其编码为Latin-1即可:

unicode_string.encode('latin1').decode('utf8')
这取决于错误解码应用的方式。如果使用了Windows代码页(例如CP1252),即使强制解码超出CP1252范围的UTF-8字节,也可能得到实际上无法重新编码回CP1252的Unicode数据。
修复这种错误的最好方法是使用ftfy,它知道如何处理各种编解码器的强制解码乱码文本问题。
对于你提供的小样本,Latin-1 似乎完全正常:
>>> unicode_string = u'\xE5\xB1\xB1\xE4\xB8\x9C \xE6\x97\xA5\xE7\x85\xA7'
>>> print unicode_string.encode('latin1').decode('utf8')
山东 日照
>>> import ftfy
>>> print ftfy.fix_text(unicode_string)
山东 日照

如果您有字面字符\x,后跟两个数字,则会有另一层编码,其中每个字节都被4个字符替换。您需要首先通过请求Python使用string_escape编解码来将其解码为实际的字节:

>>> unicode_string = ur'\xE5\xB1\xB1\xE4\xB8\x9C \xE6\x97\xA5\xE7\x85\xA7'
>>> unicode_string
u'\\xE5\\xB1\\xB1\\xE4\\xB8\\x9C \\xE6\\x97\\xA5\\xE7\\x85\\xA7'
>>> print unicode_string.decode('string_escape').decode('utf8')
山东 日照

'string_escape' 是 Python 2 中唯一的编解码器,它生成一个字节串,因此将其解码为 UTF-8 是安全的。


谢谢,Martijn。如果我打印包含字符串的字典,它会显示{u'qualifier': u'name', u'timestamp': u'1462275769186', u'value': u'\\xE5\\x8E\\x9F\\xE6\\x9D\\xA5\\xE6\\x98\\xAFolivia\\xE5\\x95\\x8A', u'columnFamily': u'interActive', u'type': u'Put', u'row': u'1771897264'},而且print m.get('value').encode('latin1').decode('utf8')仍然会打印出\xE5\x8E\x9F... - armnotstrong
@armnotstrong:你没有字节。你有字面意义的反斜杠、x字符和十六进制数字。你在这里有一个不同的问题。是什么产生了这个问题? - Martijn Pieters
@armnotstrong:已更新;从你的问题中无法清楚地看出你有文字内容。将来请显示这种字符串的repr()输出(这就是你在评论中显示的dict()表示形式所使用的每个键和值)。 - Martijn Pieters
原始数据是从HBase中检索出来的,它只能将ASCII字符串作为值进行存储。而org.apache.spark.examples.pythonconverters.HBaseResultToStringConverter则提供了这个功能。 - armnotstrong
我不熟悉Spark,抱歉。无法确定这是Spark问题还是数据存储本身的问题。 - Martijn Pieters
谢谢。你已经给了我足够的指导。我会从这里开始解决问题。 - armnotstrong

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