19

我正在尝试使用 Python xlrd 和 csv 模块将 Excel 电子表格转换为 CSV,但我对编码问题感到困惑。Xlrd 以 Unicode 格式从 Excel 生成输出,而 CSV 模块需要 UTF-8。

我认为这与 xlrd 模块无关:一切正常输出到标准输出或其他不需要特定编码的输出。

工作表编码为 UTF-16-LE,根据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))(egscell 而不是 s.cell_value)时,整个文档的写入都没有错误。

输出不是特别理想 ( text:u'516-7773167'),但即使有问题的字符仍在输出中,它也可以避免错误。

这让我觉得挑战可能在 xlrd 中。

想法?

4

4 回答 4

26

我希望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)
于 2009-07-27T17:37:03.583 回答
9

你要求解释,但有些现象没有你的帮助是莫名其妙的。

(A) 如果可能,Excel 97 及更高版本创建的 XLS 文件中的字符串以 Latin1 编码,否则以 UTF16LE 编码。每个字符串都带有一个标志,说明使用了哪个。早期的 Excels 根据用户的“代码页”对字符串进行编码。在任何情况下,xlrd 都会产生 unicode objects。仅当 XLS 文件由忽略代码页或谎报代码页的 3rd 方软件创建时,文件编码才有意义。请参阅 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 是带有 ACUTE 的拉丁小写字母 I,在“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)整个文档写入时没有错误。输出不是特别理想 (text:u'516-7773167')"""

发生这种情况是因为 csv 编写器正在调用__str__您的 Cell 对象的方法,<type>:<repr(value)>这可能对调试有用,但正如您在 csv 文件中所说的那样不太好。

(F) Alex Martelli 的解决方案很棒,因为它可以让你继续前进。但是,您应该阅读 xlrd 文档中有关 Cell 类的部分:单元格的类型是文本、数字、布尔值、日期、错误、空白和空。如果您有日期,您将希望将它们格式化为日期而不是数字,因此您不能使用 isinstance() (而且您可能不希望函数调用开销)......这就是Cell.ctype属性Sheet.cell_type()Sheet.row_types()方法是给。

(G) UTF8 不是 Unicode。UTF16LE 不是 Unicode。UTF16 不是 Unicode ...而且单个字符串会在 UTF16 BOM 上每个浪费 2 个字节的想法太荒谬了,甚至 MS 都无法考虑 :-)

(H) 进一步阅读(除了 xlrd 文档):

http://www.joelonsoftware.com/articles/Unicode.html
http://www.amk.ca/python/howto/unicode
于 2009-07-28T10:45:53.510 回答
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)

可以为您解决问题,假设我正确理解了问题,并假设在写入文件时抛出了错误。

于 2009-07-27T16:26:45.333 回答
0

看起来你有2个问题。

该单元格中有些东西搞砸了-我认为'7'应该编码为u'x37',因为它在ASCII范围内。

更重要的是,您收到一条错误消息,指出ascii无法使用编解码器,这表明您的 unicode 编码有问题 - 它认为您正在尝试编码一个0xed无法用 ASCII 表示的值,但你说你试图用 unicode 来表示它。

我不够聪明,无法找出导致问题的特定行-如果您编辑问题以告诉我是哪行导致该错误消息,我可能会提供更多帮助(我猜是this_row.append(s.cell_value(row,col))or bcw.writerow(this_row),但不胜感激你确认)。

于 2009-07-27T16:27:06.923 回答