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

时间:2018-07-23 15:20:58

标签: python zip zipfile

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

zfh = zipfile.ZipFile("test.zip", mode = "a")
zfh.write("/home/msala/test.txt")
zfh.close()

使用内置方法printdir()重复几次此“更新” 我发现在存档中不仅存储了最后一个“ test.txt”,而且还存储了文件的所有先前副本。

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

问题:

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

此刻,我列出了所有存储的文件,并按文件名,上次修改时间对其进行了排序...

2 个答案:

答案 0 :(得分:0)

tl; dr是,您不能在没有构建一些额外信息的情况下执行此操作-但这无需排序即可完成,即使您 did 必须进行排序,性能成本是无关紧要的。


首先,让我解释一下zipfile的工作原理。 (即使您了解这一点,以后遇到相同问题的读者也可能不会。)

很遗憾,the specification is a copyrighted and paywalled ISO document,所以我无法链接到它或引用它。但是,可以使用原始的PKZip APPNOTE.TXT,它是事实上的前标准化标准。而且Wikipedia之类的众多网站都有不错的摘要。

一个zipfile文件包含0个或更多片段,后跟一个中央目录。

片段就像被全部串联成一个大文件一样。

文件的主体可以包含zip条目,可以按任意顺序包含任何所需的内容。 (这是DOS / Windows自解压归档文件的工作方式-解压缩的可执行文件在第一个片段的开头。)任何看起来像zip条目但未被中央目录引用的东西都不会被视为zip。条目(修复损坏的zipfile时除外)。

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

目录是包含大多数相同信息的目录条目的列表,以及指向可找到zip条目的指针。

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


  

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

该行为在任何地方都没有明确说明。

提取整个存档应按zip目录中出现的顺序(infolist给出的顺序)提取两个文件,第二个覆盖第一个。

但是按名称提取并不一定要给你两个人,它可以给你最后一个或第一个,也可以随机选择一个。

Python为您提供了最后一个。它的工作方式是,在读取目录时,它会构建一个dict映射文件名到ZipInfo,将它们添加到遇到的位置,因此最后一个将覆盖先前的文件。 (Here's the 3.7 code.)每当您尝试按文件名访问内容时,它只会在该字典中查找文件名以获取ZipInfo

但这是您要依靠的东西吗?我不确定。一方面,此行为从Python 1.6到3.7是相同的,这通常是一个很好的信号,即使它从未被记录过,它也不会改变。另一方面,存在一些开放的问题(包括#6818,该问题可能会以某种方式向库添加删除支持)。


自己要做同样的事情真的并不难。额外的好处是您可以使用不同的规则-始终保留第一条规则,始终保留最新的mod时间,等等。

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

但是您实际上并不需要在这里进行排序。毕竟,您不希望能够以某种顺序获得具有给定名称的所有条目,而只想为每个名称获得一个特定的条目。因此,您可以只进行内部ZipFile的工作,这只需要线性的构建时间,每次搜索都需要固定的时间。您可以在此处使用任何所需的规则。

entries = {}
for entry in zfh.infolist():
    if entry.filename not in entries:
        entries[entry.filename] = entries

这将保留任何名称的 first 条目。如果要保留最后一个,只需删除if。如果要通过modtime保持最新,只需更改它if entry.date_time > entries[entry.filename].date_time:。依此类推。

现在,您可以调用extract("home/msala/test.txt")并知道自己正在获取该名称的第一个/最后一个/最新/任何文件,而不必依赖于调用extract(entries["home/msala/test.txt"])时发生的情况。


  

在zip档案文件中,是否有任何标志告知旧副本..是旧副本,并被最后一个副本取代?

不,不是。

删除文件的方法是从中央目录中删除它。您只需要重写中央目录即可。由于它位于zipfile的末尾,并且几乎总是足够小以至于即使最小的软盘都可以容纳,因此即使在DOS时代,也普遍认为这很好。

(但是请注意,如果您从计算机中间拔出电源,则会得到一个没有中央目录的zip文件,必须通过扫描所有文件条目来重建该zip文件。因此,许多更新的工具会代替,至少对于较小的文件,将整个文件重写为tempfile,然后将其重命名为原始文件,以确保安全的原子写入。)

有时,至少对于某些早期的工具,尤其是对于巨大的档案馆,有时会使用NUL重写条目路径名的第一个字节。但这并没有真正将条目标记为已删除,只是将其重命名为"\0ome/msala/test.txt"。实际上,许多现代工具都将其视为恰如其分,并给您带来奇怪的错误,告诉您它们找不到名为'ome'''的目录或其他有趣的东西。另外,这意味着目录条目中的文件名不再与文件条目标题中的文件名匹配,这将导致许多现代工具将zip文件标记为损坏。

在任何情况下,Python的zipfile模块都不做任何一个,因此您需要继承ZipFile的子类来自己添加支持。

答案 1 :(得分:0)

我用这种方法解决了,类似于数据库记录管理。

将文件添加到档案中,我寻找以前存储的副本(相同的文件名)。 对于他们每个人,我都将他们的字段“评论”设置为特定的标记,例如“已删除”。

我们添加新文件,并将comment =空。

我们可以“抽真空”:使用通常的工具收缩zip存档(在后台创建一个新的存档,丢弃注释设置为“已删除”的文件)。

这样,我们还有一个简单的“版本控制”。 我们拥有以前所有的文件副本,直到真空为止。