使用 Python 解压缩文件并返回它创建的所有目录

Posted

技术标签:

【中文标题】使用 Python 解压缩文件并返回它创建的所有目录【英文标题】:unzipping a file with Python and returning all the directories it creates 【发布时间】:2013-02-04 21:41:02 【问题描述】:

如何使用 Python 将 .zip 文件解压缩到某个目录 output_dir 并获取解压缩后生成的所有目录的列表?例如,如果我有:

unzip('myzip.zip', 'outdir')

outdir 是一个目录,其中可能包含其他文件/目录。当我将myzip.zip 解压缩到其中时,我希望unzip 返回在outdir/ 中创建的所有目录作为压缩的结果。到目前为止,这是我的代码:

import zipfile
def unzip(zip_file, outdir):
    """
    Unzip a given 'zip_file' into the output directory 'outdir'.
    """
    zf = zipfile.ZipFile(zip_file, "r")
    zf.extractall(outdir)

如何让unzip 返回它在outdir 中创建的目录?谢谢。

编辑:对我来说最有意义的解决方案是仅获取 zip 文件中的***目录,然后递归地遍历它们,这将保证我获得 zip 生成的所有文件。这可能吗? namelist 的系统特定行为使其几乎无法依赖

【问题讨论】:

实际上,如果 zip 存档中包含的一个或多个目录已在本地存在,您无法确保任何给定目录将仅包含从存档中提取的文件。在这种情况下,您需要扫描文件系统before 提取,然后after 提取并计算差异。不过看起来工作量很大。 extractall() 的制造者为什么不让该函数返回刚刚创建的文件名数组? 【参考方案1】:

您可以使用namelist() 方法读取压缩文件的内容。目录将有一个尾随路径分隔符:

>>> import zipfile
>>> zip = zipfile.ZipFile('test.zip')
>>> zip.namelist()
['dir2/', 'file1']

您可以在提取内容之前执行此操作。

根据您的操作环境,namelist() 的结果可能仅限于 zip 存档的***路径(例如 Linux 上的 Python)或可能覆盖存档的全部内容(例如 IronPython 在Windows)。

namelist() 返回 zip 存档内容的完整列表,目录用尾随路径分隔符标记。例如,以下文件结构的 zip 存档:

./file1
./dir2
./dir2/dir21
./dir3
./dir3/file3
./dir3/dir31
./dir3/dir31/file31

zipfile.ZipFile.namelist() 返回以下列表:

[ 'file1', 
  'dir2/', 
  'dir2/dir21/', 
  'dir3/', 
  'dir3/file3', 
  'dir3/dir31/', 
  'dir3/dir31/file31' ]

【讨论】:

我猜 取决于实现。在 IronPython 中,zip.namelist() 显示 所有 存档中的文件,而不仅仅是顶层 hmm... 可能是这些专利/许可问题之一。在我的 Linux 环境中只是***的。如果有人知道解决方案,我很乐意听到。 我想查看所有目录,而不仅仅是***目录。 我的环境出了点问题...在 Fedora 17 上重新安装了与 zip 相关的软件包,现在获得了完整路径。很奇怪......无论如何,我的错,很抱歉造成混乱。 这将告诉您 zip 文件中的内容,但它不会告诉您在解压缩文件时将创建哪些目录名称 - 一些目录可能在解压缩之前已经存在.【参考方案2】:

ZipFile.namelist 将返回存档中项目名称的列表。但是,这些名称将只是文件的全名,包括它们的目录路径。 (一个 zip 文件只能包含文件,不能包含目录,所以目录由归档成员名称隐含。)要确定创建的目录,您需要每个文件隐式创建的每个目录的列表。

下面的dirs_in_zip() 函数将执行此操作并将所有目录名称收集到一个集合中。

import zipfile
import os

def parent_dirs(pathname, subdirs=None):
    """Return a set of all individual directories contained in a pathname

    For example, if 'a/b/c.ext' is the path to the file 'c.ext':
    a/b/c.ext -> set(['a','a/b'])
    """
    if subdirs is None:
        subdirs = set()
    parent = os.path.dirname(pathname)
    if parent:
        subdirs.add(parent)
        parent_dirs(parent, subdirs)
    return subdirs


def dirs_in_zip(zf):
    """Return a list of directories that would be created by the ZipFile zf"""
    alldirs = set()
    for fn in zf.namelist():
        alldirs.update(parent_dirs(fn))
    return alldirs


zf = zipfile.ZipFile(zipfilename, 'r')

print(dirs_in_zip(zf))

【讨论】:

not 是否返回所有平台上的完整路径名。查看其他答案。 @isedev,我刚刚在 Linux (Ubuntu 12.04.2) 上使用 Python 2.7.3 和在 OS X 10.7.5 上使用 Python 2.7.1 进行了测试,并且在这两种情况下都获得了完整的路径名。我看不出怎么可能not 给出完整的路径名,因为在 zipinfo 结构中只有一个地方可以存储名称。 namelist() 应该与 [zinfo.name for zinfo in zfile.infolist()] 相同你知道这是正确的特定平台吗? 我的环境出了点问题...在 Fedora 17 上重新安装了 zip 相关软件包,现在获得了完整路径。很奇怪......无论如何,我的错,很抱歉造成混乱。不过还有一件事:zip 可以包含目录...试试这个:'touch file1; mkdir 目录2; zip test.zip *' -> dir2 将在 zip 中列出。无论如何,谢谢,会相应地更新我的答案。【参考方案3】:

让它完成,然后读取目录的内容 - 这是一个 good example 。

【讨论】:

但我不知道哪些文件是通过解压缩创建的,哪些已经存在。请记住,输出目录中可能包含文件 公平地说,使用 namelist() 或 infolist() 来查看存档的内容。【参考方案4】:

假设没有其他人会同时写入目标目录,在解压缩之前递归遍历目录,然后在解压之后,比较结果。

【讨论】:

以上是关于使用 Python 解压缩文件并返回它创建的所有目录的主要内容,如果未能解决你的问题,请参考以下文章

从内存中的 FTP 下载 Zip 文件并解压缩

使用 Maven 解压缩并重新压缩文件?

使用 Python 下载并解压缩文件

压缩后如何立即使用 Phar 解压缩存档?

下载一个zip,解压缩并解析它 - 全部在内存中 - Java

在 Python 中解压缩 .bz2 文件