为什么 Zip 能够压缩比具有相同内容的多个文件更小的单个文件?

six*_*ude 130 linux zip

假设我有 10,000 个 XML 文件。现在假设我想将它们发送给朋友。在发送它们之前,我想压缩它们。

方法 1:不要压缩它们

结果:

Resulting Size: 62 MB
Percent of initial size: 100%
Run Code Online (Sandbox Code Playgroud)

方法 2:压缩每个文件并向他发送 10,000 个 xml 文件

命令:

for x in $(ls -1) ;  do   echo $x ; zip "$x.zip" $x ; done
Run Code Online (Sandbox Code Playgroud)

结果:

Resulting Size: 13 MB
Percent of initial size: 20%
Run Code Online (Sandbox Code Playgroud)

方法 3:创建包含 10,000 个 xml 文件的单个 zip

命令:

zip all.zip $(ls -1)
Run Code Online (Sandbox Code Playgroud)

结果:

Resulting Size: 12 MB
Percent of initial size: 19%
Run Code Online (Sandbox Code Playgroud)

方法 4:将文件连接成一个文件并压缩它

命令:

cat *.xml > oneFile.txt ; zip oneFile.zip oneFile.txt
Run Code Online (Sandbox Code Playgroud)

结果:

Resulting Size: 2 MB
Percent of initial size: 3%
Run Code Online (Sandbox Code Playgroud)

问题:

  • 为什么我在压缩单个文件时会得到如此显着的结果?
  • 我希望使用方法 3 获得比方法 2 更好的结果,但没有。为什么?
  • 这种行为是否特定于zip?如果我尝试使用gzip会得到不同的结果吗?

附加信息:

$ zip --version
Copyright (c) 1990-2008 Info-ZIP - Type 'zip "-L"' for software license.
This is Zip 3.0 (July 5th 2008), by Info-ZIP.
Currently maintained by E. Gordon.  Please send bug reports to
the authors using the web page at www.info-zip.org; see README for details.

Latest sources and executables are at ftp://ftp.info-zip.org/pub/infozip,
as of above date; see http://www.info-zip.org/ for other sites.

Compiled with gcc 4.4.4 20100525 (Red Hat 4.4.4-5) for Unix (Linux ELF) on Nov 11 2010.

Zip special compilation options:
    USE_EF_UT_TIME       (store Universal Time)
    SYMLINK_SUPPORT      (symbolic links supported)
    LARGE_FILE_SUPPORT   (can read and write large files on file system)
    ZIP64_SUPPORT        (use Zip64 to store large files in archives)
    UNICODE_SUPPORT      (store and read UTF-8 Unicode paths)
    STORE_UNIX_UIDs_GIDs (store UID/GID sizes/values using new extra field)
    UIDGID_NOT_16BIT     (old Unix 16-bit UID/GID extra field not used)
    [encryption, version 2.91 of 05 Jan 2007] (modified for Zip 3)
Run Code Online (Sandbox Code Playgroud)

编辑:元数据

一个答案表明,不同之处在于存储在 zip 中的系统元数据。我不认为情况会如此。为了测试,我做了以下事情:

for x in $(seq 10000) ; do touch $x ; done
zip allZip $(ls -1)
Run Code Online (Sandbox Code Playgroud)

生成的 zip 为 1.4MB。这意味着仍有大约 10 MB 的无法解释的空间。

Ala*_*tko 130

Zip 在压缩时分别处理每个文件的内容。每个文件都有自己的压缩流。压缩算法(通常为DEFLATE)支持识别重复部分。但是,Zip 不支持查找文件之间的冗余。

这就是为什么当内容在多个文件中时会有如此多的额外空间:它多次将相同的压缩流放入文件中。

  • @JAB:像 7z 和 rar 这样的压缩工具使用术语“solid”归档来将多个文件从头到尾打包成更大的压缩流。对于 64MiB 等中等大小的块,对单个文件的随机访问可能需要从它所在的压缩块的开头解压多达 64MiB 的数据。您可以在随机访问和查找跨文件冗余之间进行适当的权衡。7z 可以使用更有效(但压缩速度较慢)的 LZMA 压缩方案,这是优于 zip 的另一个优势。 (28认同)
  • 这也是为什么某些压缩工具为您提供了将文件单独压缩或作为单个实体压缩的选项的原因。(尽管通常这也意味着如果您只想查看其中的单个文件,您必须解压缩更多的档案。) (9认同)
  • @sixtyfootersdude 许多压缩算法,例如 DEFLATE,都以流的形式运行。要恢复足够的信息以解压缩流的一部分,您需要处理整个流直到该点。如果他们试图找到文件之间的冗余,您必须解压缩所有 1000 个文件才能找到最后一个。实际上,这通常是 tgz 的工作方式。但是,zip 旨在让您提取单个文件。tgz 被设计为更加全有或全无 (6认同)

Aga*_*nju 48

ZIP 压缩基于要压缩的数据中的重复模式,文件越长,压缩效果越好,因为可以找到和使用的模式越来越长。

简而言之,如果您压缩一个文件,将(短)代码映射到(长)模式的字典必然包含在每个生成的 zip 文件中;如果您压缩一个长文件,该词典将被“重用”并且在所有内容中变得更加有效。

如果您的文件甚至有点相似(就像文本一样),“字典”的重用会变得非常有效,结果是总 zip 小得多。

  • @gerrit 它有自己的问题。Zip 旨在让您快速访问存档中的任何文件 - 尝试从 100 GiB UHA 存档中解压缩单个文件,您就会明白他们为什么选择这种方式。它还专为附加而设计 - 您可以拥有备份 zip 并根据需要继续添加(或替换)文件。所有这些在使用档案时都是一个巨大的帮助。权衡是,如果您要压缩非常相似的文件(* 不是* 所有常见的),则它无法利用相似性来减少存档大小。 (16认同)
  • 我不反对,这可能是一个好方法。我没有设计或编写 ZIP。我只是说它的作用... (4认同)
  • ZIP 既可以存档也可以压缩。这是否意味着 ZIP 会单独压缩每个文件,即使它们最终都位于同一个 ZIP 文件中? (3认同)
  • 它有点不得不 - 想象一下你删除了一个文件,你不希望它再花半个小时用一个新的“字典”重新压缩其余的文件。- 此外,它可能假设不同的文件需要非常不同的“字典”。 (2认同)
  • 我不明白为什么必须这样做。使用 Unix 工具,我会先用 tar 归档一个文件,然后用 gzip/bz2/lzma 压缩它。压缩算法不关心存档中编码了多少文件。此外,从压缩档案中删除单个文件的情况有多普遍?我想我从来没有这样做过。 (2认同)
  • @gerrit 从存档中删除单个文件可能并不常见,但是我遇到过需要将单个文件更新为一个文件的情况。 (2认同)

小智 43

在 Zip 中,每个文件都是单独压缩的。相反的是“固体压缩”,即文件被压缩在一起。7-zip 和 Rar 默认使用固态压缩。Gzip和Bzip2不能压缩多个文件,所以先用Tar,效果和solid压缩一样。

由于 xml 文件具有相似的结构和可能相似的内容,如果将文件压缩在一起,则压缩率会更高。

例如,如果一个文件包含该字符串"<content><element name="并且压缩器已经在另一个文件中找到该字符串,它将用一个指向前一个匹配项的小指针替换它,如果压缩器不使用“固体压缩”,则该字符串在该文件中的第一次出现文件将被记录为较大的文字。


Mik*_*ott 9

Zip 不仅存储文件的内容,还存储文件元数据,如拥有用户 ID、权限、创建和修改时间等。如果您有一个文件,则您有一组元数据;如果您有 10,000 个文件,那么您就有 10,000 组元数据。

  • 好点,但系统元数据只占用 1.4MB 的空间。请参阅我的编辑。 (3认同)

Mon*_*der 7

OP 遗漏的一个选项是在关闭压缩的情况下将所有文件压缩在一起,然后在压缩设置为最大的情况下压缩生成的 zip。这大致模拟了 *nix .tar.Z、.tar.gz、.tar.bz 等压缩档案的行为,允许压缩利用跨文件边界的冗余(ZIP 算法在单个文件中运行时无法做到这一点)。经过)。这允许稍后提取单个 XML 文件,但会最大化压缩。缺点是提取过程需要额外的步骤,临时使用比普通 .zip 所需的磁盘空间多得多的磁盘空间。

随着 7-Zip 等免费工具将 tar 系列扩展到 Windows,真的没有理由不使用 .tar.gz 或 .tar.bz 等,因为 Linux、OS X 和 BSD 都有操作它们的本地工具。


Bon*_*Oak 5

zip 压缩格式分别存储和压缩每个文件。它不利用文件之间的重复,只利用文件内的重复。

连接文件允许 zip 充分利用所有文件的重复,从而大大提高压缩率。

例如,假设每个 XML 文件都有一个特定的标题。该标头在每个文件中仅出现一次,但在许多其他文件中几乎相同地重复。在方法 2 和 3 中 zip 无法为此进行压缩,但在方法 4 中可以。

  • 这与 5 小时前已经发布的前 3 个答案之一有何不同? (3认同)