Python:为unicode清理一个字符串?

时间:2010-07-11 19:54:49

标签: python unicode character-encoding

  

可能重复:
  Python UnicodeDecodeError - Am I misunderstanding encode?

我有一个字符串,我正在努力使unicode()函数安全:

>>> s = " foo “bar bar ” weasel"
>>> s.encode('utf-8', 'ignore')

Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    s.encode('utf-8', 'ignore')
UnicodeDecodeError: 'ascii' codec can't decode byte 0x93 in position 5: ordinal not in range(128)
>>> unicode(s)

Traceback (most recent call last):
  File "<pyshell#9>", line 1, in <module>
    unicode(s)
UnicodeDecodeError: 'ascii' codec can't decode byte 0x93 in position 5: ordinal not in range(128)

我大部分都在这里挥舞着。如何从字符串中删除不安全的字符?

与此question有些相关,虽然我无法解决我的问题。

这也失败了:

>>> s
' foo \x93bar bar \x94 weasel'
>>> s.decode('utf-8')

Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    s.decode('utf-8')
  File "C:\Python25\254\lib\encodings\utf_8.py", line 16, in decode
    return codecs.utf_8_decode(input, errors, True)
UnicodeDecodeError: 'utf8' codec can't decode byte 0x93 in position 5: unexpected code byte

2 个答案:

答案 0 :(得分:38)

好问题。编码问题很棘手。让我们从“我有一个字符串开始。” Python 2中的字符串不是真正的“字符串”,它们是字节数组。所以你的字符串,它来自哪里,它是什么编码?您的示例在文字中显示了引号,我甚至不确定您是如何做到的。我尝试将其粘贴到Python解释器中,或者使用Option- [在OS X上键入它,但它没有通过。

看看你的第二个例子,你有一个十六进制93的字符。那不能是 UTF-8 ,因为在UTF-8中,任何高于127的字节都是多字节的一部分序列。所以我猜它应该是Latin-1。问题是,x93不是Latin-1字符集中的字符。 Latin-1中从x7f到x9f的这个“无效”范围被认为是非法的。但是,微软看到了未使用的范围,并决定在那里加上“曲线引号”。在这样做的过程中,他们创建了一个名为“windows-1252”的类似编码,就像Latin-1一样,其中包含无效范围的内容。

所以,我们假设它是 windows-1252 。现在怎么办? String.decode将字节转换为Unicode,这就是你想要的。你的第二个例子是在正确的轨道上,但它失败了,因为字符串不是UTF-8。尝试:

>>> uni = 'foo \x93bar bar\x94 weasel'.decode("windows-1252")
u'foo \u201cbar bar\u201d weasel'
>>> print uni
foo “bar bar” weasel
>>> type(uni)
<type 'unicode'>

这是正确的,因为打开卷曲引用是Unicode U + 201C。现在您已经拥有了Unicode,您可以使用您选择的任何编码将其序列化为字节(如果您需要通过网络传递它),或者如果它保留在Python中,则将其保留为Unicode。如果要转换为UTF-8,请使用反对函数string.encode。

>>> uni.encode("utf-8")
'foo \xe2\x80\x9cbar bar \xe2\x80\x9d weasel'

Curly引号需要3个字节才能以UTF-8编码。你可以使用UTF-16,它们只有两个字节。但是,您不能编码为ASCII或Latin-1,因为它们没有卷曲引号。

答案 1 :(得分:4)

修改即可。看起来您的字符串的编码方式使(LEFT DOUBLE QUOTATION MARK)变为\x93,而(右双引号)变为\x94。有许多具有这种映射的代码页,CP1250就是其中之一,所以你可以使用它:

s = s.decode('cp1250')

对于将映射到\x93的所有代码页,请参阅here(所有这些代码页也将映射到\x94,可以对其进行验证{{ 3}})。