我正在尝试使用Python xlrd和csv模块将Excel电子表格翻译为CSV,但我对编码问题感到困惑。 Xlrd以Unicode格式生成Excel中的输出,CSV模块需要UTF-8。
我想象这与xlrd模块无关:一切正常输出到stdout或其他不需要特定编码的输出。
根据book.encoding
我正在做的简化版本是:
from xlrd import *
import csv
b = open_workbook('file.xls')
s = b.sheet_by_name('Export')
bc = open('file.csv','w')
bcw = csv.writer(bc,csv.excel,b.encoding)
for row in range(s.nrows):
this_row = []
for col in range(s.ncols):
this_row.append(s.cell_value(row,col))
bcw.writerow(this_row)
这会产生以下错误:约740行:
UnicodeEncodeError: 'ascii' codec can't encode character u'\xed' in position 5: ordinal not in range(128)
值似乎是挂起来的是“516-777316” - 原始Excel工作表中的文字是“516-7773167”(最后有7个)
我会第一个承认我对字符编码的工作原理只有模糊的感觉,所以到目前为止我所尝试的大部分内容都是.encode
和.decode
的各种笨拙的排列。在s.cell_value(row,col)
如果有人可以建议一个解决方案我会很感激 - 如果你能解释一下什么不起作用以及为什么这样做会更好,那么我将来可以更容易地调试这些问题。
提前致谢!
修改
感谢您的评论到目前为止。
当我使用this_row.append(s.cell(row,col))
(例如s.cell而不是s.cell_value)时,整个文档会无误地写入。
输出不是特别理想的(text:u'516-7773167'
),但即使有问题的字符仍然在输出中,它也可以避免错误。
这让我觉得挑战可能毕竟在xlrd中。
思想?
答案 0 :(得分:25)
我希望cell_value
返回值是给您带来问题的unicode字符串(请打印type()
以确认),在这种情况下,您应该可以通过更改此行来解决此问题:
this_row.append(s.cell_value(row,col))
为:
this_row.append(s.cell_value(row,col).encode('utf8'))
如果cell_value
返回多个不同的类型,那么当且仅当它返回一个unicode字符串时,你需要编码;所以你将这一行分成几行:
val = s.cell_value(row, col)
if isinstance(val, unicode):
val = val.encode('utf8')
this_row.append(val)
答案 1 :(得分:9)
你要求解释,但如果没有你的帮助,有些现象是莫名其妙的。
(A)由Excel 97以后创建的XLS文件中的字符串以Latin1编码,否则以UTF16LE编码。每个字符串都带有一个标志,告诉哪个被使用早期Excel根据用户的“代码页”编码字符串。无论如何, xlrd会生成unicode对象。仅当第三方软件创建了XLS文件时,文件编码才会引起关注,第三方软件要么省略代码页,要么就是代码页。请参阅xlrd文档前面的Unicode部分。
(B)无法解释的现象:
此代码:
bcw = csv.writer(bc,csv.excel,b.encoding)
导致Python 2.5,2.6和3.1出现以下错误:TypeError: expected at most 2 arguments, got 3
- 这是关于我对csv.writer上的文档的期望;它期待一个类似文件的对象,后跟(1)没有(2)方言或(3)一个或多个格式化参数。你给它一个方言,而csv.writer没有编码参数,所以splat。您使用的是哪个版本的Python?或者你没有复制/粘贴你实际运行的脚本吗?
(C)回溯周围无法解释的现象以及实际的违规数据是什么:
"the_script.py", line 40, in <module>
this_row.append(str(s.cell_value(row,col)))
UnicodeEncodeError: 'ascii' codec can't encode character u'\xed' in position 5: ordinal not in range(128)
首先,在违规代码行中有一个str()不在简化脚本中 - 你没有复制/粘贴你实际运行的脚本吗?在任何情况下,你都不应该使用str - 你不会得到你的花车的全部精度;让csv模块转换它们。
第二,你说“”这个值似乎被挂起了“516-777316” - 原始Excel表格中的文字是“516-7773167”(最后有7个) “” - 很难想象7如何在最后丢失。我会使用这样的东西来确切地找出有问题的数据:
try:
str_value = str(s.cell_value(row, col))
except:
print "row=%d col=%d cell_value=%r" % (row, col, s.cell_value(row, col))
raise
%r使您免于键入cell_value=%s ... repr(s.cell_value(row, col))
... repr()会生成明确的数据表示形式。学习它。使用它。
你是怎么到达“516-777316”的?
第三,错误信息实际上是抱怨偏移5处的unicode字符u'\ xed'(即第六个字符)。 U + 00ED是拉丁语的小写字母,我很敏感,在“516-7773167”中根本就没有这样的东西
第四,错误位置似乎是一个移动目标 - 你在其中一个解决方案的评论中说:“错误在bcw.writerow上。”咦?
(D)为什么你得到了这个错误消息(使用str()):str(a_unicode_object)
尝试将unicode对象转换为str对象,并且在没有任何编码信息的情况下使用ascii,但是你有非ascii数据,所以splat。请注意,您的对象是生成以utf8编码的csv文件,但您的简化脚本未在任何地方提及utf8。
(E)“”“... s.cell(row,col))(egscell而不是s.cell_value)
整个文档写入没有错误。输出不是特别需要(文本:u' 516-7773167' ) “”“
这种情况正在发生,因为csv编写器正在调用Cell对象的__str__
方法,这会生成<type>:<repr(value)>
,这可能对调试很有用,但正如你在csv文件中说的那么好。 / p>
(F)Alex Martelli的解决方案很棒,因为它让你前进。但是,您应该阅读xlrd docs中Cell类的部分:单元格的类型是text,number,boolean,date,error,blank和empty。如果你有日期,你会想要将它们格式化为日期而不是数字,所以你不能使用isinstance()(你可能不想要函数调用开销)......这就是{{1} }属性和Cell.ctype
和Sheet.cell_type()
方法适用于。
(G)UTF8不是Unicode。 UTF16LE不是Unicode。 UTF16不是Unicode ......而且单个字符串在UTF16 BOM上每个浪费2个字节的想法对于MS甚至考虑来说都是荒谬的: - )
(H)进一步阅读(除xlrd文档外):
Sheet.row_types()
答案 2 :(得分:0)
似乎有两种可能性。一个是你没有正确打开输出文件:
“如果csvfile是一个文件对象,它必须在平台上以'b'标志打开,这会产生影响。” (http://docs.python.org/library/csv.html#module-csv)
如果这不是问题,那么另一个选择是使用codecs.EncodedFile(file,input [,output [,errors]])作为输出.csv的包装器:
http://docs.python.org/library/codecs.html#module-codecs
这将允许您将文件对象从传入的UTF16过滤到UTF8。虽然它们在技术上都是“unicode”,但它们编码的方式却截然不同。
这样的事情:
rbc = open('file.csv','w')
bc = codecs.EncodedFile(rbc, "UTF16", "UTF8")
bcw = csv.writer(bc,csv.excel)
可以为您解决问题,假设我理解正确的问题,并假设在写入文件时抛出错误。
答案 3 :(得分:0)
看起来你有2个问题。
在那个单元格中出现了一些问题 - “7”应该被编码为u'x37'我认为,因为它在ASCII范围内。
更重要的是,您收到错误消息,指出无法使用ascii
编解码器这一事实表明您对unicode的编码有问题 - 它认为您正在尝试对值进行编码0xed
无法用ASCII表示,但你说你试图用unicode表示它。
我不够聪明,无法弄清楚导致问题的特定行 - 如果你编辑你的问题告诉我导致该错误消息的哪一行我可能能够帮助更多(我猜它是{{ 1}}或this_row.append(s.cell_value(row,col))
,但感谢您确认)。