【问题标题】:Knowing current compressed file size using gzwrite (zlib)使用 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*,给定一条路径。我如何将其重定向到管道?

标签: c++ zlib


【解决方案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 调用会无限期挂起?
    • 您不需要管道。看我的回答。
    猜你喜欢
    • 1970-01-01
    • 2021-10-08
    • 2019-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多