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

时间:2018-11-01 11:00:42

标签: c++ zlib

我正在将zlib用于c ++。

语录来自 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时发生的,而不是在关闭文件时发生的。

2 个答案:

答案 0 :(得分:2)

zlib提供了功能gzoffset(),它可以完全满足您的要求。

如果由于某种原因您坚持使用的zlib版本已有大约八年的历史,那么在添加gzoffset()时,使用gzdopen()可以轻松实现。您使用fopen()open()打开输出文件,并提供文件描述符(如果使用fileno(),则使用dup()fopen()),然后提供gzdopen()的描述符。然后,您可以随时使用ftell()lseek()查看写入的内容。请注意不要尝试双重关闭描述符。请参阅gzdopen()的注释。

答案 1 :(得分:0)

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

要进行此设置,您首先需要打开文件以通过简单的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");

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

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和拼接一次,并在允许的数据量后一次性拼接)存储为第五个参数。如果您不希望压缩不必要的数据,只需按上面的块所示进行操作即可。

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