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

Eri*_*rik 1 python encoding xlrd import-from-excel

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

import xlrd
workbook = xlrd.open_workbook('thefile.xls')
Run Code Online (Sandbox Code Playgroud)

这适用于大多数文件,但对于从特定组织获取的文件失败.我尝试从该组织打开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
Run Code Online (Sandbox Code Playgroud)

这看起来好像xlrd正在尝试打开以UTF-16以外的其他方式编码的Excel文件.我怎样才能避免这个错误?文件是以有缺陷的方式编写的,还是仅存在导致问题的特定字符?如果我打开并重新保存Excel文件,xlrd会打开文件而不会出现问题.

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

我试图打开的文件可在此处获得:

https://dl.dropboxusercontent.com/u/6779408/Stackoverflow/AEPUsageHistoryDe​​tail_RequestID_00183816.xls

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

App*_*234 11

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

他们正在使用一些Java Excel API(参见下面的链接),可能在IBM大型机或类似设备上.

从堆栈跟踪中,writeaccess信息无法解码为Unicode,因为@字符.

有关XLS文件格式的writeaccess信息的更多信息,请参阅5.112 WRITEACCESSPage 277.

该字段包含已保存文件的用户的用户名.

import xlrd
dump = xlrd.dump('thefile.xls')
Run Code Online (Sandbox Code Playgroud)

在原始文件上运行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  @@@@@@@@@@@@@@@@
Run Code Online (Sandbox Code Playgroud)

在用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
Run Code Online (Sandbox Code Playgroud)

基于编码为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
Run Code Online (Sandbox Code Playgroud)

1252是Windows CP-1252(拉丁语I)(BIFF4-BIFF5),它不是EBCDIC_037.

xlrd试图使用unicode的事实意味着它确定文件的版本是BIFF8.

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

  1. 在使用xlrd打开文件之前修复该文件.您可以使用dump检查非标准输出的文件,然后如果是这种情况,您可以使用xlutils.save或其他库覆盖writeaccess信息.

  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
Run Code Online (Sandbox Code Playgroud)

workbook=xlrd.open_workbook('thefile.xls',encoding_override="cp1252")
Run Code Online (Sandbox Code Playgroud)

似乎成功打开文件.

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