Python: 如何处理Unicode字符串?

14 投票
2 回答
27970 浏览
提问于 2025-04-16 01:08

可能重复的问题:
Python UnicodeDecodeError - 我是不是误解了编码?

我有一个字符串,想让它适合 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)

我现在有点手忙脚乱。请问我需要怎么做才能把字符串中的不安全字符去掉呢?

这和这个问题有点关系,不过我没能从中找到解决我的问题的方法。

这个也不行:

>>> 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 个回答

5

编辑。看起来你的字符串编码方式有点特别,导致 (左双引号)变成了 \x93,而 (右双引号)变成了 \x94。有很多种编码方式会这样映射,其中 CP1250 就是其中之一,你可以使用这个编码:

s = s.decode('cp1250')

想查看所有将 映射为 \x93 的编码方式,可以点击 这里(所有这些编码也会将 映射为 \x94,你可以在 这里 验证)。

41

这是个好问题。编码问题确实很复杂。我们先从“我有一个字符串。”开始说起。在Python 2中,字符串其实并不是真正的“字符串”,它们是字节数组。那么你的字符串是从哪里来的,它用的是什么编码呢?你给的例子里有弯引号,我甚至不太确定你是怎么做到的。我尝试把它粘贴到Python解释器里,或者在OS X上用Option-[输入,但都不行。

不过看你第二个例子,你有一个十六进制的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'

在UTF-8中,弯引号需要3个字节来编码。如果你用UTF-16,它们只需要两个字节。不过,你不能用ASCII或Latin-1来编码,因为它们没有弯引号。

撰写回答