Python字符串解码问题

4 投票
4 回答
20292 浏览
提问于 2025-04-15 20:03

我正在尝试解析一个CSV文件,这个文件里有一些数据,主要是数字,但也有一些字符串。我不知道这些字符串的编码方式,但我知道它们是希伯来语的。

最后我需要知道这些字符串的编码,这样我才能把它们转换成Unicode格式,打印出来,或者以后放进数据库里。

我试着用Chardet,它说这些字符串是Windows-1255(cp1255),但是当我尝试用print someString.decode('cp1255')时,出现了一个很有名的错误:

UnicodeEncodeError: 'ascii' codec can't encode characters in position 1-4: ordinal not in range(128)

我尝试了所有其他可能的编码,但都没有成功。而且,这个文件绝对是有效的,因为我可以在Excel中打开这个CSV文件,并且看到正确的数据。

有没有什么办法可以正确解码这些字符串呢?


编辑:这里有一个例子。其中一个字符串看起来像这样(希伯来字母表的前五个字母):

print repr(sampleString)
#prints:
'\xe0\xe1\xe2\xe3\xe4'

(使用Python 2.6.2)

4 个回答

0

你在打印的时候遇到了编码错误,这很可能是解码没问题,只是你不能正确地打印出结果。试着在命令提示符下运行 chcp 65001,然后再开始你的Python代码。

3

当你用 someString.decode('cp1255') 把字符串解码成unicode时,你得到了一个抽象的希伯来文本的表示形式(这部分是成功的!)。但是,当你用 print 打印的时候,你需要的是一个具体的、用特定编码表示的内容。看起来你遇到的问题不是解码,而是打印的部分。

要打印的话,如果你的终端支持cp1255编码,可以直接用 print someString。或者你可以用 print someString.decode('cp1255').encode('the_encoding_your_terminal_does_understand'),这样可以确保打印出来的内容是你终端能理解的编码。如果你不需要打印的内容能被识别为希伯来文,使用 print repr(someString.decode('cp1255')) 也能给你一个有意义的抽象unicode字符串的表示。

13

这是发生了什么:

  • sampleString 是一个字节串(使用 cp1255 编码)
  • sampleString.decode("cp1255") 将字节串解码(解码就是把字节转换成可读的字符串)成一个 Unicode 字符串
  • print sampleString.decode("cp1255") 尝试将这个 Unicode 字符串打印到控制台。打印的时候需要编码这个 Unicode 字符串(编码就是把可读的字符串转换成字节)。你看到的错误意味着 Python 的打印语句无法将这个 Unicode 字符串写入控制台的编码中。sys.stdout.encoding 是终端的编码。

所以问题在于你的控制台不支持这些字符。你可以尝试调整控制台使用其他编码。具体怎么做取决于你的操作系统和终端程序。

另一种方法是手动指定要使用的编码:

print sampleString.decode("cp1255").encode("utf-8")

另请参见:

你可以尝试的一个简单测试程序:

import sys
print sys.stdout.encoding
samplestring = '\xe0\xe1\xe2\xe3\xe4'
print samplestring.decode("cp1255").encode(sys.argv[1])

在我的 utf-8 终端上:

$ python2.6 test.py utf-8
UTF-8
אבגדה

$ python2.6 test.py latin1
UTF-8
Traceback (most recent call last):
UnicodeEncodeError: 'latin-1' codec can't encode characters in position 0-4: ordinal not in range(256)

$ python2.6 test.py ascii
UTF-8
Traceback (most recent call last):
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128)

$ python2.6 test.py cp424
UTF-8
ABCDE

$ python2.6 test.py iso8859_8
UTF-8
�����

对于 latin-1 和 ascii 的错误信息意味着字符串中的 Unicode 字符无法在这些编码中表示。

注意最后两个。我将 Unicode 字符串编码为 cp424 和 iso8859_8 编码(这两种编码在 http://docs.python.org/library/codecs.html#standard-encodings 中列出,支持希伯来字符)。使用这些编码时没有异常,因为希伯来 Unicode 字符在这些编码中有对应的表示。

但是,当我的 utf-8 终端接收到不同于 utf-8 的字节时,它会感到非常困惑。

在第一个例子(cp424)中,我的 UTF-8 终端显示 ABCDE,这意味着 A 的 utf-8 表示对应于 cp424 中的 ה,也就是说,字节值 65 在 utf-8 中表示 A,而在 cp424 中表示 ה。

encode 方法有一个可选的字符串参数,可以用来指定当编码无法表示某个字符时应该怎么处理(文档)。支持的策略有严格(默认)、忽略、替换、xmlcharref 和 backslashreplace。你甚至可以 添加你自己的自定义策略

另一个测试程序(我在字符串周围加上引号,以更好地展示忽略的行为):

import sys
samplestring = '\xe0\xe1\xe2\xe3\xe4'
print "'{0}'".format(samplestring.decode("cp1255").encode(sys.argv[1], 
      sys.argv[2]))

结果:

$ python2.6 test.py latin1 strict
Traceback (most recent call last):
  File "test.py", line 4, in <module>
    sys.argv[2]))
UnicodeEncodeError: 'latin-1' codec can't encode characters in position 0-4: ordinal not in range(256)
[/tmp]
$ python2.6 test.py latin1 ignore
''
[/tmp]
$ python2.6 test.py latin1 replace
'?????'
[/tmp]
$ python2.6 test.py latin1 xmlcharrefreplace
'&#1488;&#1489;&#1490;&#1491;&#1492;'
[/tmp]
$ python2.6 test.py latin1 backslashreplace
'\u05d0\u05d1\u05d2\u05d3\u05d4'

撰写回答