我有一个使用此工具Windows
在System.IO.Compression.ZipFile
机器上创建的zip文件(此zip归档文件包含许多文件和文件夹)。我有一个在Linux
计算机(确切地说是树莓派)上运行的python代码,必须解压缩存档文件并创建所有必要的文件夹和文件。我正在使用Python 3.5.0
和zipfile
库,这是示例代码:
import zipfile
zip = zipfile.ZipFile("MyArchive.zip","r")
zip.extractall()
zip.close()
现在,当我运行此代码而不是获得漂亮的未压缩目录树时,我会在根目录中获得所有带有奇怪名称(例如Folder1\Folder2\MyFile.txt
)的文件。
我的假设是,由于zip归档文件是在Windows上创建的,而Windows上的目录分隔符是\
,而在Linux上是/
,因此Python zipfile
库将\
视为文件名的一部分,而不是目录分隔符。另请注意,当我手动提取该归档文件(而不是通过python代码)时,所有文件夹均按预期方式创建,因此看来这绝对是zipfile
库的问题。另一个注意事项是,对于使用其他工具(而非System.IO.Compression.ZipFile
)创建的zip归档文件,使用相同的python代码可以正常工作。
对正在发生的事情以及如何解决它有任何见解吗?
答案 0 :(得分:3)
正在发生的事情是,尽管Windows将\
(path.sep
)和/
(path.altsep
)都识别为路径分隔符,但Linux仅识别/
( path.sep
。
如@blhsing's answer所示,ZipFile
的现有实现始终确保将path.sep
和/
视为有效的分隔符。这意味着在Linux上,\
被视为文件名的文字部分。要更改此设置,您可以将os.altsep
设置为\
,因为会检查它是否不为空。
如果您像其他答案所建议的那样自行修改ZipFile
,只需添加一行以将\
盲目地更改为path.sep
,因为/
是总是已经改变了。这样,/
,\
以及可能的path.altsep
将全部转换为path.sep
。这就是命令行工具正在执行的操作。
答案 1 :(得分:1)
这确实是zipfile
module的错误,其中ZipFile._extract_member()
中有以下行,当使用操作系统特定的路径分隔符盲目替换文件名中的'/'
时,还应该寻找'\\'
:
arcname = member.filename.replace('/', os.path.sep)
您可以通过以下方法解决此问题:将ZipFile._extract_member()
替换为直接从源代码复制但已修正上述行的版本:
from zipfile import ZipFile, ZipInfo
import shutil
import os
def _extract_member(self, member, targetpath, pwd):
"""Extract the ZipInfo object 'member' to a physical
file on the path targetpath.
"""
if not isinstance(member, ZipInfo):
member = self.getinfo(member)
if os.path.sep == '/':
arcname = member.filename.replace('\\', os.path.sep)
else:
arcname = member.filename.replace('/', os.path.sep)
if os.path.altsep:
arcname = arcname.replace(os.path.altsep, os.path.sep)
# interpret absolute pathname as relative, remove drive letter or
# UNC path, redundant separators, "." and ".." components.
arcname = os.path.splitdrive(arcname)[1]
invalid_path_parts = ('', os.path.curdir, os.path.pardir)
arcname = os.path.sep.join(x for x in arcname.split(os.path.sep)
if x not in invalid_path_parts)
if os.path.sep == '\\':
# filter illegal characters on Windows
arcname = self._sanitize_windows_name(arcname, os.path.sep)
targetpath = os.path.join(targetpath, arcname)
targetpath = os.path.normpath(targetpath)
# Create all upper directories if necessary.
upperdirs = os.path.dirname(targetpath)
if upperdirs and not os.path.exists(upperdirs):
os.makedirs(upperdirs)
if member.is_dir():
if not os.path.isdir(targetpath):
os.mkdir(targetpath)
return targetpath
with self.open(member, pwd=pwd) as source, \
open(targetpath, "wb") as target:
shutil.copyfileobj(source, target)
return targetpath
ZipFile._extract_member = _extract_member