2016-12-07 74 views
2

我正尝试使用python脚本从中国服务提供商(我不是中国自己的)下载文件。提供者给我一个.zip文件,其中包含一个文件,其中似乎有中文字符。这似乎是导致zipfile模块barf。Python zipfile模块不能提取带有中文字符的文件名

代码:

import zipfile 

f = "/path/to/zip_file.zip" 

if zipfile.is_zipfile(f): 
    fz = zipfile.ZipFile(f, 'r') 

zip文件本身不包含任何非ASCII字符,但里面的文件它。当我运行上面的脚本,我得到以下异常:

Traceback (most recent call last): File "./temp.py", line 9, in <module> 
    fz = zipfile.ZipFile(f, 'r') File "/usr/lib/python2.7/zipfile.py", line 770, in __init__ 
    self._RealGetContents() File "/usr/lib/python2.7/zipfile.py", line 859, in _RealGetContents 
    x.filename = x._decodeFilename() File "/usr/lib/python2.7/zipfile.py", line 379, in _decodeFilename 
    return self.filename.decode('utf-8') File "/usr/lib/python2.7/encodings/utf_8.py", line 16, in decode 
    return codecs.utf_8_decode(input, errors, True) UnicodeDecodeError: 'utf8' codec can't decode byte 0xbd in position 30: invalid start byte 

我已经试过翻翻答案,很多类似的问题:

请纠正我,如果我错了,但它看起来li ke an open issue with the zipfile module

我该如何解决这个问题?是否有任何替代模块处理我应该使用的zip文件?或者其他解决方案?

TIA。

编辑: 我可以通过linux命令行实用程序“unzip”完全访问/解压缩同一文件。

回答

1

ZIP文件无效。它有一个标志,表明它内部的文件名被编码为UTF-8,但它们实际上不是;它们包含无效的UTF-8字节序列。也许他们是GBK?也许别的东西?也许有些不协调的混合物?不幸的是,在非ASCII文件名一致地处理时,野外的ZIP工具非常差。

一个快速的解决方法可能是替换解码文件名的库函数。这是一个猴子补丁,因为没有注入自己ZipInfo类ZipFile的成一种简单的方法,但是:

zipfile.ZipInfo._decodeFilename = lambda self: self.filename 

将禁用试图文件名进行解码,并始终以一个字节的字符串返回ZipInfo filename属性,您可以继续进行解码/手动以任何适当的方式处理。

+0

“它有一个预示着里面的文件名被编码成UTF-8的标志”我从来没有听说过标志。哪里会找到它? – 2016-12-09 23:30:45

+0

对不起,我没有在我的问题中提到这一点,但我可以通过linux命令行实用程序“unzip”完全访问/解压缩它。所以我怀疑这是否是文件本身的问题。 – hyperwiser

+1

@Rhymoid:文件属性标志字的第11位,请参见[PKware appnote](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)sec 4.1.4:“如果设置了此位,则这个文件的文件名和注释字段必须使用UTF-8进行编码“。 hyperwiser:通过任何一种工具处理文件来判断文件的有效性是不明智的。当然,一个早于UTF-8标志的工具会忽略它,就像一个不关心编码的面向字节的工具一样。除了野外工具对ZIP的各种不确定边缘情况的各种反应之外,还有很多不同之处。 – bobince

2

处理模块zipfile中的非utf-8文件名的Python 2.x(2.7)和Python 3.x的方式有点不同。

首先,它们都检查文件的ZipInfo.flag_bits,如果ZipInfo.flag_bits & 0x800,文件的名称将用utf-8解码。

如果上面的检查是False,在Python 2.x中,名称的字节字符串将被返回;在Python 3.x中,模块将使用编码cp437解码文件并返回解码结果。当然,在两个Python版本中,模块都不知道文件名的真实编码。

因此,假设您已经从ZipInfo对象或zipfile.namelist方法得到了一个文件名,并且您已经知道文件名是使用XXX编码进行编码的。这些都是你得到正确的Unicode文件名的方法:

# in python 2.x 
filename = filename.decode('XXX') 


# in python 3.x 
filename = filename.encode('cp437').decode('XXX') 
相关问题