【问题标题】:Unicode to UTF8 for CSV Files - Python via xlrdCSV 文件的 Unicode 到 UTF8 - Python 通过 xlrd
【发布时间】:2010-11-14 10:01:14
【问题描述】:

我正在尝试使用 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)

我将是第一个承认我对字符编码的工作原理只有模糊认识的人,所以到目前为止我尝试过的大部分都是@987654326 上.encode.decode 的各种摸索排列@

如果有人可以提出解决方案,我将不胜感激 - 如果您能解释什么不工作以及为什么不工作,那就更好了,这样我以后可以更轻松地自己调试这些问题。

提前致谢!

编辑:

感谢到目前为止的 cmets。

当我使用 this_row.append(s.cell(row,col))(例如 s.cell 而不是 s.cell_value)时,整个文档都会正确写入。

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

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

想法?

【问题讨论】:

  • 查看整个回溯,知道谁在抛出错误会很有用。
  • 没什么可看的:文件“the_script.py”,第 40 行,在 this_row.append(str(s.cell_value(row,col))) UnicodeEncodeError: 'ascii' codec无法在位置 5 对字符 u'\xed' 进行编码:序数不在范围内(128)
  • 使用“Unicode 格式的 Excel 输出”,您的意思似乎是“UTF-16 格式的 Excel 输出”。 Unicode 定义了一个代码空间,它由不同的编码系统表示,例如 UTF-8 或 UTF-16。
  • @anschauung:感谢您的评论和投票。现在轮到你帮忙解释了,尤其是关于“516-7773167”之谜。
  • @John:没有忽略你的询问——只是仍然被这个问题难住了。它肯定与那个特定的字符串有关,但我能想出的最佳答案是电子表格——来自第三方应用程序——错误地识别了编码,导致 xlrd 抓取了太多字符或错误的字符,试图理解它。不幸的是,我仍然缺乏检验该假设的技能,但当我这样做时,我会再次触及基础。

标签: python unicode encoding csv xlrd


【解决方案1】:

我希望 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)

【讨论】:

  • 完美!做到了。我想我没想到需要区别对待不同的值类型。谢谢!
【解决方案2】:

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

(A) 如果可能,Excel 97 及更高版本创建的 XLS 文件中的字符串以 Latin1 编码,否则以 UTF16LE 编码。每个字符串都带有一个标志,说明使用了哪个。早期的 Excels 根据用户的“代码页”对字符串进行编码。在任何情况下,xlrd 都会生成 unicode 对象。仅当 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 数据,所以啪啪啪。请注意,您的目标是生成以 utf8 编码的 csv 文件,但您的简化脚本没有在任何地方提及 utf8。

(E) """... s.cell(row,col)) (egscell 而不是s.cell_value) 整个文档写入时没有错误。输出不是特别理想 (text:u'516- 7773167')"""

发生这种情况是因为 csv 编写器正在调用您的 Cell 对象的 __str__ 方法,这会生成 &lt;type&gt;:&lt;repr(value)&gt; 这可能对调试有用,但正如您所说的在您的 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

【讨论】:

  • +1:感谢您的精彩解释和背景链接。这让我意识到我无法避免再自学编码,即使在直接的问题已经解决之后,我也很感激你能提供这样的细节。
【解决方案3】:

看起来你有 2 个问题。

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

更重要的是,您收到一条错误消息,指出无法使用 ascii 编解码器这一事实表明您的 unicode 编码有问题 - 它认为您正在尝试对值进行编码 @987654323 @ 不能用 ASCII 表示,但你说你正试图用 unicode 表示它。

我不够聪明,无法找出导致问题的特定行 - 如果您编辑问题以告诉我是哪行导致该错误消息,我可能会提供更多帮助(我猜是 @987654324 @ 或 bcw.writerow(this_row),但希望您能确认)。

【讨论】:

  • 谢谢!错误在 bcw.writerow 上。如果我使用 print this_row,一切都会正确输出。据我所知,'7' 没有明显的问题——当我打印到标准输出时,它正确输出(如 u'516-7773167')。
  • 那么看起来bcw.writerow 正在等待ASCII - 你确定你对csv.writer 的论点正确吗(见docs.python.org/library/csv.html#csv.writer)?我对0xed 的来源感到困惑。
【解决方案4】:

似乎有两种可能性。一是您可能没有正确打开输出文件:

“如果 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)

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

【讨论】:

  • 嗯,它给出了不同的错误消息(这次是在向文件写入任何内容之前):UnicodeDecodeError: 'utf16' codec can't decode byte 0x0a in position 938: truncated data
猜你喜欢
  • 2013-09-23
  • 2015-07-04
  • 2010-10-28
  • 1970-01-01
  • 2017-06-07
  • 2017-04-10
  • 1970-01-01
相关资源
最近更新 更多