使用 gzwrite (zlib) 了解当前压缩文件的大小

Posted

技术标签:

【中文标题】使用 gzwrite (zlib) 了解当前压缩文件的大小【英文标题】:Knowing current compressed file size using gzwrite (zlib) 【发布时间】:2018-11-01 11:00:42 【问题描述】:

我在 c++ 中使用 zlib。

引用自 http://refspecs.linuxbase.org/LSB_3.0.0/LSB-PDA/LSB-PDA/zlib-gzwrite-1.html 关于gzwrite 功能:

gzwrite() 函数应将数据写入由file 引用的压缩文件,该压缩文件应以写入模式打开(请参阅gzopen()gzdopen())。在进入时,buf 应指向包含len 字节未压缩数据的缓冲区。 gzwrite() 函数应压缩此数据并将其写入文件。 gzwrite() 函数应返回实际写入的未压缩字节数。

我将此解释为返回值不会告诉我写入时文件变大了多少。只有多少数据被压缩到文件中。

知道文件有多大的唯一方法是关闭它,然后从文件系统中读取大小。我要求只继续写入文件,直到它达到一定大小。不关闭文件可以实现吗?

一种解决方法是写入直到未压缩的大小达到我的限制,然后关闭文件,从文件系统读取大小并基于此更新我对文件大小的最佳猜测,然后重新打开文件并继续写入.这将使我在接近尾声时关闭并打开文件几次(因为我接近大小限制)。

另一种解决方法,它会给出更多的估计(这不是我真正想要的)是写入直到未压缩的大小达到限制,关闭文件,从文件系统读取文件大小并计算压缩比至今。我可以使用此压缩比来计算未压缩文件大小的新限制,其中压缩应该使我降低到压缩文件大小的限制。如果我重复这一点,估计会有所改善,但同样不是我想要的。

有更好的选择吗?

如果 zlib 可以在文件仍处于打开状态时告诉我压缩文件的大小,则首选选项。我不明白为什么此时此信息在 zlib 中不可用,因为压缩是在我调用 gzwrite 时发生的,而不是在我关闭文件时发生的。

【问题讨论】:

您可以将压缩数据写入管道,然后从该管道读取数据并将其写入实际文件,同时跟踪写入了多少数据 你会怎么做?我必须给gzopen打电话,给我一个gzFile_s*,给定一条路径。我如何将其重定向到管道? 【参考方案1】:

zlib 提供了函数gzoffset(),它完全符合您的要求。

如果由于某种原因,当您添加了 gzoffset() 时,您使用了大约 8 年以上的 zlib 版本,那么使用 gzdopen() 很容易做到这一点。您使用fopen()open() 打开输出文件,并提供文件描述符(使用fileno()dup(),如果您使用fopen()),然后将该描述符提供给gzdopen()。然后您可以随时使用ftell()lseek() 来查看写了多少。注意不要尝试双重关闭描述符。请参阅 gzdopen() 的 cmets。

【讨论】:

【参考方案2】:

您可以使用管道解决此问题。这个想法是将压缩数据写入管道。之后,您从管道的另一端读取数据,对其进行计数并将其写入实际文件。

要进行设置,您需要首先通过简单的open 打开要写入的文件。然后通过pipe2 创建一个管道并通过将其中一个管道描述符传递给gzdopen 来初始化zlib:

int out = open("/path/to/file", O_WRONLY | O_CREAT | O_TRUNC);
int p[2];
pipe2(p, O_NONBLOCK);
gzFile zFile = gzdopen(p[0], "w");

您现在可以先将数据写入管道,然后将其从管道拼接到输出文件:

gzwrite(zFile, buf, 1024); //or any other length
size_t bytesWritten = 0;
do 
    bytesWritten = splice(p[1], NULL, out, NULL, 1024, SPLICE_F_NONBLOCK | SPLICE_F_MORE);
 while(bytesWritten == 1024);

如您所见,您现在可以通过bytesWritten 告诉您实际写入了多少数据。只需将其汇总到另一个变量中,并在您写入所需的数据量后立即停止拼接(或者通过将所有内容写入 zFile 并使用允许的数据量一次性拼接一次) store 作为第五个参数。如果你不想压缩不必要的数据,只需按上面所示的块进行。

关于 splice 的说明: Splice 是特定于 linux 的,基本上只是一个非常有效的副本。您始终可以用简单的“读写”组合替换它,即将数据从fd[1] 读取到缓冲区中,然后将该缓冲区中的数据写入out - 拼接速度更快,代码更少。

【讨论】:

管道的缓冲区大小没有限制吗?所以在输出达到那个大小之后(我在某处读取了大约 65536 字节的内容)gzwrite 调用会无限期挂起? 您不需要管道。看我的回答。

以上是关于使用 gzwrite (zlib) 了解当前压缩文件的大小的主要内容,如果未能解决你的问题,请参考以下文章

珍珠:我怎样才能得到 zlib 理解的压缩数据

PHP GZ压缩与解压

使用minizip解压缩多个文件(基于zlib)

将 zlib 压缩的 base64 字符串转换为 Uiimage 的问题

Q新闻丨Facebook开源新的压缩算法,性能超zlib;来自Google的RPC框架:gRPC1.0发布

使用 zlib+dictionary 压缩/解压缩 spdy 名称/值块时出错