python、zipfile、文件更新:ZIP存档中存储了多个版本

python、zipfile、文件更新:ZIP存档中存储了多个版本,python,zip,zipfile,Python,Zip,Zipfile,假设我们有一个test.zip文件,并更新了一个文件: zfh = zipfile.ZipFile("test.zip", mode = "a") zfh.write("/home/msala/test.txt") zfh.close() 使用内置方法重复几次此“更新” 我看到在归档文件中不仅存储了最后一个“test.txt”,还存储了该文件以前的所有副本 好的,我知道zipfile库没有删除方法 问题: 如果调用内置方法extra

假设我们有一个test.zip文件,并更新了一个文件:

zfh = zipfile.ZipFile("test.zip", mode = "a")
zfh.write("/home/msala/test.txt")
zfh.close()
使用内置方法重复几次此“更新” 我看到在归档文件中不仅存储了最后一个“test.txt”,还存储了该文件以前的所有副本

好的,我知道zipfile库没有删除方法

问题:

  • 如果调用内置方法extract(“/home/msala/test.txt”), 提取文件的哪个副本并将其写入文件系统
  • 在zip存档中,是否有任何标志表明旧副本。。旧版本是否被最后一个版本取代

目前,我列出了所有存储的文件,并按文件名、上次修改时间对它们进行排序…

tl;dr是不行的,如果不构建一点额外的信息,您就无法做到这一点,但这可以在不进行排序的情况下完成,而且,即使您必须进行排序,性能成本也将无关紧要


首先,让我解释一下zipfiles是如何工作的。(即使您理解这一点,以后有同样问题的读者也可能不理解。)

不幸的是,我无法链接到它或引用它。然而,作为事实上的标准化标准的原始版本是可用的。很多网站都有很好的摘要

zipfile是0个或多个片段,后跟一个中心目录

片段被视为所有片段都连接到一个大文件中

文件主体可以包含任意顺序的zip条目,以及您想要的任何内容。(这就是DOS/Windows自解压存档的工作方式解压可执行文件位于第一个片段的开头。)任何看起来像zip条目但未被中心目录引用的内容都不会被视为zip条目(修复损坏的zipfile时除外。)

每个zip条目都以一个标题开头,该标题为您提供以下数据的文件名、压缩格式等

目录是包含大部分相同信息的目录项列表,加上一个指向查找zip项位置的指针

目录条目的顺序决定了归档文件的顺序


如果调用内置方法extract(“/home/msala/test.txt”),将提取文件的哪个副本并将其写入文件系统

该行为实际上没有在任何地方指定

提取整个归档文件应按照zip目录中的顺序(与给定的顺序相同)提取两个文件,第二个文件覆盖第一个文件

但是,按名称提取并不一定要同时提供这两个名称,它可以提供最后一个或第一个名称,也可以随机选择一个名称

Python为您提供了最后一个选项。其工作方式是,在读取目录时,它构建一个dict映射文件名到
ZipInfo
s,只需在遇到时添加它们,因此最后一个文件名将覆盖以前的文件名。()每当您尝试按文件名访问某个内容时,它只需在该dict中查找文件名即可获得
ZipInfo

但这是你想要依赖的东西吗?我不确定。一方面,从Python1.6到3.7,这种行为都是相同的,这通常是一个很好的迹象,表明它不会改变,即使它从未被记录在案。另一方面,还存在一些未解决的问题,包括旨在以某种方式向库添加删除支持,从而可能会改变库


你自己做同样的事情其实并不难。另外一个好处是,你可以使用不同的规则,总是保留第一个,总是保留一个最新的mod time,等等

您似乎担心排序信息列表的性能成本,这可能不值得担心。读取和解析zip目录所需的时间将使排序的成本几乎不可见

但你真的不需要在这里分类。毕竟,您不希望以某种顺序获取具有给定名称的所有条目,您只希望为每个名称获取一个特定条目。因此,您可以做
ZipFile
内部所做的事情,这只需要线性时间来构建,每次搜索时都需要固定的时间。你可以在这里使用任何你想要的规则

entries = {}
for entry in zfh.infolist():
    if entry.filename not in entries:
        entries[entry.filename] = entries
这将保留任何名称的第一个条目。如果要保留最后一个,只需删除
If
。如果您想通过modtime保存最新的,只需将其更改为
If entry.date\u time>entries[entry.filename].date\u time:
。等等

现在,您不必依赖于调用
extract(“home/msala/test.txt”)
时会发生什么,您可以调用
extract(条目[“home/msala/test.txt”)
,并知道您得到的是该名称的第一个/最后一个/最新的/任何文件


在zip存档中,是否有任何标志表明旧副本。。旧版本是否被最后一个版本取代

不,不是真的

删除文件的方法是将其从中心目录中删除。您只需重写中心目录即可完成此操作。由于它位于zipfile的末尾,并且几乎总是足够小,甚至可以放在最小的软盘上,所以即使在DOS时代,它也被认为是很好的

(但注意,如果你拔出中间的计算机,你就得到了一个没有中心目录的zip文件,它必须通过扫描所有的文件条目来重建。因此,许多更新的工具,至少对于较小的文件,改写整个文件到一个TEMPFILE,然后在原始文件上重命名,以保证一个安全的原子写。)

至少一些早期的工具有时会用NUL重写条目路径名的第一个字节,特别是对于大型归档文件。但这并没有真正将条目标记为已删除,它只是将其重命名为
“\0ome/msala/test.txt”
。事实上,许多现代工具会将其视为确切的含义,并给出奇怪的错误,告诉您它们找不到目录n