使用 xlrd 打开 Excel 文件时出现编码错误

Encoding error when opening an Excel file with xlrd

我正在尝试使用 xlrd 打开 Excel 文件 (.xls)。这是我正在使用的代码的摘要:

import xlrd
workbook = xlrd.open_workbook('thefile.xls')

这适用于大多数文件,但不适用于我从特定组织获得的文件。当我尝试打开来自该组织的 Excel 文件时出现的错误如下。

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/app/.heroku/python/lib/python2.7/site-packages/xlrd/__init__.py", line 435, in open_workbook
    ragged_rows=ragged_rows,
  File "/app/.heroku/python/lib/python2.7/site-packages/xlrd/book.py", line 116, in open_workbook_xls
    bk.parse_globals()
  File "/app/.heroku/python/lib/python2.7/site-packages/xlrd/book.py", line 1180, in parse_globals
    self.handle_writeaccess(data)
  File "/app/.heroku/python/lib/python2.7/site-packages/xlrd/book.py", line 1145, in handle_writeaccess
    strg = unpack_unicode(data, 0, lenlen=2)
  File "/app/.heroku/python/lib/python2.7/site-packages/xlrd/biffh.py", line 303, in unpack_unicode
    strg = unicode(rawstrg, 'utf_16_le')
  File "/app/.heroku/python/lib/python2.7/encodings/utf_16_le.py", line 16, in decode
    return codecs.utf_16_le_decode(input, errors, True)
UnicodeDecodeError: 'utf16' codec can't decode byte 0x40 in position 104: truncated data

这看起来好像 xlrd 正在尝试打开一个 Excel 文件编码不是 UTF-16。我怎样才能避免这个错误?文件是否以有缺陷的方式写入,或者是否只是一个特定的字符导致了问题?如果我打开并重新保存 Excel 文件,xlrd 可以毫无问题地打开该文件。

我试过用不同的编码覆盖打开工作簿,但这也不起作用。

我要打开的文件可以在这里找到:

https://dl.dropboxusercontent.com/u/6779408/Whosebug/AEPUsageHistoryDetail_RequestID_00183816.xls

此处报告的问题:https://github.com/python-excel/xlrd/issues/128

他们使用什么来生成该文件?

他们正在使用一些 Java Excel API(见下文,link here),可能在 IBM 大型机或类似设备上。

从堆栈跟踪中,写入访问信息无法解码为 Unicode,因为 @ 字符。

有关 XLS 文件格式的写入访问信息的更多信息,请参阅 5.112 WRITEACCESS or Page 277

此字段包含保存文件的用户的用户名。

import xlrd
dump = xlrd.dump('thefile.xls')

运行 xlrd.dump 原始文件给出

   36: 005c WRITEACCESS len = 0070 (112)
   40:      d1 81 a5 81 40 c5 a7 83 85 93 40 c1 d7 c9 40 40  ????@?????@???@@
   56:      40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40  @@@@@@@@@@@@@@@@
   72:      40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40  @@@@@@@@@@@@@@@@
   88:      40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40  @@@@@@@@@@@@@@@@
  104:      40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40  @@@@@@@@@@@@@@@@
  120:      40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40  @@@@@@@@@@@@@@@@
  136:      40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40  @@@@@@@@@@@@@@@@

在使用 Excel 或我的 LibreOffice Calc 重新保存后,写访问信息被类似

的内容覆盖
 36: 005c WRITEACCESS len = 0070 (112)
 40:      04 00 00 43 61 6c 63 20 20 20 20 20 20 20 20 20  ?~~Calc         
 56:      20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
 72:      20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
 88:      20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
104:      20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
120:      20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20                  
136:      20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20

根据编码为 40 的空格,我认为编码是 EBCDIC,当我们将 d1 81 a5 81 40 c5 a7 83 85 93 40 c1 d7 c9 40 40 转换为 EBCDIC 时,我们得到 Java Excel API.

所以是的,在 BIFF8 和更高版本的情况下,文件以有缺陷的方式写入,它应该是一个 unicode 字符串,而在 BIFF3 到 BIFF5 中,它应该是 CODEPAGE 信息中编码的字节字符串,它是

 152: 0042 CODEPAGE len = 0002 (2)
 156:      12 52                                            ?R

1252 是 Windows CP-1252 (Latin I) (BIFF4-BIFF5),它不是 EBCDIC_037.

xlrd 尝试使用 unicode,这意味着它确定文件的版本为 BIFF8。

在这种情况下,您有两个选择

  1. 先修复文件再用 xlrd 打开它。您可以使用转储检查不是标准输出的文件,然后如果是这种情况,您可以使用 xlutils.save 或其他库覆盖写入访问信息。

  2. 修补程序 xlrd 以处理您的特殊情况,在 handle_writeaccess 添加一个 try 块并在 unpack_unicode 失败时将 strg 设置为空字符串。

以下片段

 def handle_writeaccess(self, data):
        DEBUG = 0
        if self.biff_version < 80:
            if not self.encoding:
                self.raw_user_name = True
                self.user_name = data
                return
            strg = unpack_string(data, 0, self.encoding, lenlen=1)
        else:
            try:
                strg = unpack_unicode(data, 0, lenlen=2)
            except:
                strg = ""
        if DEBUG: fprintf(self.logfile, "WRITEACCESS: %d bytes; raw=%s %r\n", len(data), self.raw_user_name, strg)
        strg = strg.rstrip()
        self.user_name = strg

workbook=xlrd.open_workbook('thefile.xls',encoding_override="cp1252")

似乎打开文件成功。

没有编码覆盖它会抱怨 ERROR *** codepage 21010 -> encoding 'unknown_codepage_21010' -> LookupError: unknown encoding: unknown_codepage_21010

这对我有用。

import xlrd

my_xls = xlrd.open_workbook('//myshareddrive/something/test.xls',encoding_override="gb2312")