使用 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 压缩的 base64 字符串转换为 Uiimage 的问题