UnicodeEncodeError: 'ascii'编解码器无法对字符u'\xa3'进行编码

22

我有一个Excel电子表格,其中包含一些英镑符号。

当我尝试使用xlrd模块读取它时,我会收到以下错误:

x = table.cell_value(row, col)
x = x.decode("ISO-8859-1")
UnicodeEncodeError: 'ascii' codec can't encode character u'\xa3' in position 0: ordinal not in range(128)

如果我将其重写为 x.encode('utf-8'),它就不会再抛出错误了,但不幸的是,当我将数据写到其他地方(作为拉丁语-1)时,所有的英镑符号都变成了乱码。
我该如何修复这个问题,正确读取英镑符号?
--- 更新 ---
一些好心的读者建议我根本不需要解码它,或者只在需要时将其编码为拉丁语-1。但问题是,我最终需要将数据写入CSV文件,而它似乎不接受原始字符串。
如果我根本不编码或解码数据,则会出现以下情况(在将字符串添加到名为items的数组后):
for item in items:
    #item = [x.encode('latin-1') for x in item]
    cleancsv.writerow(item)
File "clean_up_barnet.py", line 104, in <module>
 cleancsv.writerow(item)
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2022' in position 43: ordinal not in range(128)

即使我取消注释 Latin-1 行,仍然会出现相同的错误。

我不这么认为。前面的代码行是 x = table.cell_value(row, col),来自于 xlrd 模块,但是也许它做了一些有趣的事情? - AP257
6个回答

19

解决csvwriter中出现的“'ascii' codec can't encode character…”问题的一种非常简单的方法是使用unicodecsv,它是csvwriter的替代品。

使用pip安装unicodecsv,然后您可以以完全相同的方式使用它,例如:

import unicodecsv
file = open('users.csv', 'w')
w = unicodecsv.writer(file)
for user in User.objects.all().values_list('first_name', 'last_name', 'email', 'last_login'):
    w.writerow(user)

10

就此而言:我是xlrd的作者。

xlrd是否生成unicode?
选项1:阅读xlrd文档第一屏幕底部的Unicode部分:该模块将所有文本字符串呈现为Python unicode对象。
选项2:print type(text), repr(text)

你说:“如果我将其重写为x.encode('utf-8'),它就不会再抛出错误,但不幸的是,当我将数据写出到其他地方(如latin-1格式)时,英镑符号已经变得混乱。”当然,如果你将UTF-8编码的文本写入一个期望Latin1的设备中,它将会被破坏。你期望什么?

你在编辑中说:“即使我取消了Latin-1行的注释,我仍然会收到相同的错误”。这非常不可能--更有可能的是,在不同的源代码行中(未注释的Latin1行而不是writerow行)收到略有不同的错误(提到Latin1编解码器而不是ASCII编解码器)。仔细阅读错误信息有助于理解。

你在这里的问题是,一般来说,你的数据不能用Latin1编码; 很少有真实世界的数据。你的英镑符号可以使用Latin1编码,但那并不是你的非ASCII数据的全部。问题字符是U+2022 BULLET,它无法在Latin1中编码。

如果你提前提到你正在使用Mac OS X,那么这会帮助你更快地获得更好的答案...适合CSV编码的嫌疑犯通常是cp1252(Windows),而不是mac-roman


1
仔细阅读错误信息有助于理解。当错误信息不是神秘的时候是正确的。也许在你的代码中没有这种情况,但是有很多错误信息显然从未被设计成被人类阅读的。 - Davos

9
您的代码片段中使用了 x.decode,但是您却得到了一个编码错误,这意味着x已经是Unicode格式,所以要对它进行“解码”,必须首先将其转换为字节串(这就是默认编解码器ansi失败的地方)。接下来您在文本中说“如果我将其重写为 x.encode”...这似乎暗示您知道x是Unicode格式。
那么您究竟在做什么——以及您想要做什么——是将Unicode格式的x编码成一组编码后的字节串,还是将一组编码后的字节串解码成Unicode对象?
我认为很不幸的是,您可以在字节串上调用encode,在Unicode对象上调用decode,因为我发现这似乎只会让用户感到困惑...但至少在这种情况下,您似乎成功地传递了这种困惑(至少对我来说如此;-))。
如果,正如看起来的那样,x是Unicode格式,那么您永远不需要“解码”它——您可能希望将其编码以获得一组具有特定编解码器的字节串,例如latin-1,如果这是您需要进行某种输入/输出操作的目的(对于自己内部程序使用,我建议始终坚持使用Unicode——仅在绝对需要接收到编码后的字节串用于输入/输出目的时才进行编码/解码)。

1
谢谢。我猜它是Unicode编码,但我需要对其进行编码以将其写入CSV文件。当我尝试将其编码为Latin-1时,出现了另一个错误。请参见我上面的更新... - AP257
1
搞定了 - 写入 CSV 之前我需要的那行代码是 item = [x.encode('mac_roman') for x in item]。接受这个答案是因为它帮助我弄清楚了发生了什么 - 谢谢。 - AP257
@alex-martelli 我很乐意听取您对这个问题的建议。 - JJD

6
x = x.decode("ISO-8859-1")
UnicodeEncodeError: 'ascii' codec can't encode character u'\xa3' in position 0: ordinal not in range(128)

仔细看:您在调用decode方法时出现了Unicode***Encode***Error。

原因是decode的作用是将字节序列(str)转换为unicode对象。但正如约翰所说,xlrd已经使用了Unicode字符串,因此x已经是一个unicode对象。

在这种情况下,Python 2.x假定您想解码一个str对象,因此它“贴心地”为您创建了一个。但是,为了将unicode转换为str,它需要一种编码,并选择ASCII,因为它是最低公共字符编码。您的代码实际上被解释为:

x = x.encode('ascii').decode("ISO-8859-1")

出现错误的原因是x包含一个非ASCII字符。

由于x已经是一个unicode对象,所以不需要进行decode操作。但是,现在你遇到了一个问题,即Python 2.x的csv模块不支持Unicode。你需要将数据转换为str对象。

for item in items:
    item = [x.encode('latin-1') for x in item]
    cleancsv.writerow(item)

这本来没错,不过你的数据里有个叫做“•”(U+2022 BULLET)的字符,而Latin-1无法表示它。有几种方法可以解决这个问题:
  • x.encode('latin-1', 'ignore') 来去掉这个圆点(或其他非Latin-1字符)。
  • x.encode('latin-1', 'replace') 将圆点替换成问号。
  • 用Latin-1字符(如“*”或“·”)替换这些圆点。
  • 使用一个包含你所需字符的字符编码。
现在,UTF-8得到了广泛支持,因此几乎没有理由使用其他编码来处理文本文件。

2

xlrd 支持 Unicode,所以你得到的字符串是 Unicode 字符串。英镑符号的代码点是 U+00A3,所以该字符串的表示应为 u'\xa3'。 这已经被正确读取了; 在整个程序中,您应该使用该字符串进行处理。

当您将此(抽象的、Unicode)字符串写入某个位置时,您需要选择一种编码方式。 在那时,您应该将其.encode为该编码方式,比如说 latin-1


>>> book = xlrd.open_workbook( "test.xls" )
>>> sh = book.sheet_by_index( 0 )
>>> x = sh.cell_value( 0, 0 )
>>> x
u'\xa3'
>>> print x
£

# sample outputs (for e.g. writing to a file)
>>> x.encode( "latin-1" )
'\xa3'
>>> x.encode( "utf-8" )
'\xc2\xa3'

# garbage, because x is already Unicode
>>> x.decode( "ascii" )
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xa3' in position 0:
ordinal not in range(128)
>>>

0
在使用xlrd时,我遇到了这样的一行代码...xl_data.find(str(cell_value))...,它报错了:"ascii"编解码器无法将字符u'\xdf'编码到位置3处:不在128范围内。论坛中的所有建议对我的德语单词都无用。但是改为:...xl_data.find(cell.value)...就没有错误了。因此,我认为在使用特定命令与xlrd时,使用字符串作为参数会出现特定的编码问题。

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