43

我想从 zipfile 中删除文件的唯一方法是创建一个临时 zipfile 而不删除要删除的文件,然后将其重命名为原始文件名。

在 python 2.4 中,ZipInfo 类有一个属性file_offset,因此可以创建第二个 zip 文件并将数据复制到其他文件而无需解压缩/重新压缩。

file_offset在 python 2.6 中是缺失的,那么除了通过解压缩每个文件然后重新压缩它来创建另一个 zipfile 之外,还有其他选择吗?

是否有直接删除zipfile中文件的方法,我搜索并没有找到任何东西。

4

4 回答 4

50

以下片段对我有用(从 Zip 存档中删除所有 *.exe 文件):

zin = zipfile.ZipFile ('archive.zip', 'r')
zout = zipfile.ZipFile ('archve_new.zip', 'w')
for item in zin.infolist():
    buffer = zin.read(item.filename)
    if (item.filename[-4:] != '.exe'):
        zout.writestr(item, buffer)
zout.close()
zin.close()

如果您将所有内容都读入内存,则可以消除对第二个文件的需要。但是,这个片段重新压缩了所有内容。

经过仔细检查,这ZipInfo.header_offset是从文件开始的偏移量。名称具有误导性,但主 Zip 标头实际上存储在文件末尾。我的十六进制编辑器证实了这一点。

因此,您将遇到的问题如下:您还需要删除主标题中的目录条目,否则它将指向一个不再存在的文件。如果您也保留要删除的文件的本地标头,则保持主标头完整可能会起作用,但我不确定。你是怎么用旧模块做的?

在不修改主标题的情况下,打开它时出现错误“zipfile 中缺少 X 字节”。可能会帮助您了解如何修改主标题。

于 2009-02-04T23:31:37.063 回答
10

不是很优雅,但这就是我的做法:

import subprocess
import zipfile

z = zipfile.ZipFile(zip_filename)

files_to_del = filter( lambda f: f.endswith('exe'), z.namelist()]

cmd=['zip', '-d', zip_filename] + files_to_del
subprocess.check_call(cmd)

# reload the modified archive
z = zipfile.ZipFile(zip_filename)
于 2017-08-17T16:54:19.287 回答
3

delete_from_zip_file¹ 中的例程ruamel.std.zipfile允许您根据 ZIP 中的完整路径或基于 ( re) 模式删除文件。例如,您可以删除所有使用的.exe文件test.zip

from ruamel.std.zipfile import delete_from_zip_file

delete_from_zip_file('test.zip', pattern='.*.exe')  

(请注意 前的点*)。

这与 mdm 的解决方案类似(包括需要重新压缩),但在内存中重新创建 ZIP 文件(使用 class InMemZipFile()),在完全读取后覆盖旧文件。


¹免责声明:我是该软件包的作者。

于 2017-01-01T10:33:50.430 回答
1

基于Elias Zamaria对该问题的评论。

通读https://bugs.python.org/issue6818后,我想提供有关它的更新。

今天,解决方案已经存在,尽管由于缺少作者的贡献者协议而没有被 Python 批准。

不过,您可以从https://github.com/python/cpython/blob/659eb048cc9cac73c46349eb29845bc5cd630f09/Lib/zipfile.py获取代码并从中创建一个单独的文件。之后,只需从您的项目中引用它,而不是内置的 python 库:import myproject.zipfile as zipfile.

用法:

with zipfile.ZipFile(f"archive.zip", "a") as z:
    z.remove(f"firstfile.txt")

我相信它将包含在未来的 python 版本中。对我来说,它就像给定用例的魅力。

于 2021-09-09T09:19:26.053 回答