获取 gzip 解压文件大小的速度与gunzip 一样快(无需查找)

Pro*_*ner 4 python compression performance gzip gunzip

正如一些 StackOverflow 答案所示,您可以使用 .gzip 获得准确的 gzip 解压缩文件大小decompressedSize = gzipFile.seek(0, io.SEEK_END)。有些人还建议对小于 4 GiB 的文件执行此操作.seek(-4, 1)。但是,由于它会遍历整个文件直至结束,因此对于较大的文件来说非常耗时(对于大约 1 GiB 的解压数据,需要几秒钟才能找到结束)。

然后我尝试使用gunzip -l somefile.gz(同一文件),它设法立即输出当前文件大小以及解压缩时的文件大小。

我怎样才能以gunzip最快的速度获得解压缩的 gzip 文件的大小?

(PS 我尝试获取解压后的 gzip 大小的原因是解压时的 CLI 进度条)

Mar*_*ler 7

gzip -l实际上正在寻找并读取文件的最后四个字节。您的评论“因为它一直在文件中查找,所以对于较大的文件来说非常耗时”表明您不明白查找是什么。查找并不是读取整个文件,直到读到最后。查找是将文件的读取指针移动到所需的位置,并从那里开始读取。它需要 O(1) 时间,而不是 O( n ) 时间(其中n是文件的大小)。@crissal 的答案展示了如何正确执行此操作。

最后四个字节是最后一个 gzip 成员的未压缩长度,模 2 32,假设 gzip 文件末尾没有垃圾。

您会注意到该句子中的三个警告。首先,正如您已经注意到的,未压缩的大小需要小于 2 32字节,该数字才有意义。但是,您不一定可以通过查看压缩文件来判断这是否属实。gzip 可以压缩到 1024 倍以上,因此 gzip 文件可能只有 2 22长度

第二个警告是 gzip 文件必须只有一个成员。gzip 格式允许连接 gzip 成员,其中最后四个字节仅代表最后一个成员的长度。除了解码整个 gzip 文件之外,没有可靠的方法可以找到其他成员。

第三个警告是 gzip 文件末尾没有任何垃圾。一般来说,我还没有在野外看到过这种情况,但 gzip 文件的末尾可能有填充,这会再次混淆长度的查找。

底线:如果可靠地确定压缩大小对您很重要,那么仅当您可以控制 gzip 文件的生成并且可以确保内容 < 4 GB 时,您才可以使用最后四个字节,只有一个成员,最后没有垃圾。

对于您的应用程序,您不需要知道未压缩数据的长度。相反,您应该将进度条基于到目前为止已处理的压缩数据的比例。您可以从文件系统中知道文件的压缩大小,并且知道到目前为止已消耗了多少压缩数据。如果数据近似均匀,则压缩比在整个解压缩过程中将近似恒定。对于恒定的压缩比,压缩数据进度条将显示与未压缩数据进度条完全相同的内容。


cri*_*sal 6

未压缩的输入大小存储在最后 4 个字节 [ 1 ] 中,因此开始的建议-4是正确的。

然而,问题是光标必须在第二个参数之前移动 4 个位置,因此,4 个位置相对于文件末尾,而不是当前位置。因此,1 (SEEK_CUR)应替换为2 (SEEK_END).

一旦你设置好位置,你就可以read()只使用最后 4 个字节,然后将它们转换为int[ 2 ];字节顺序是小尾数。

with open("yourfile", "rb") as f:
  # place the cursor in the right position
  f.seek(-4, 2)

  # get the size of uncompressed input from last 4 bytes
  size = int.from_bytes( f.read(), "little" )
Run Code Online (Sandbox Code Playgroud)