如何使用zlib创建一个可以与gz兼容的文件?

4
我想使用C++中的zlib来生成与gz兼容的输出文件。
我安装了zlib的开发人员包,据我所知,它可以在Unix和Windows上用于创建gz兼容文件。
sudo aptitude install libz-dev

虽然我编写了一个C++程序,但在相关点上,我相当遵循了使用示例。我还未更改地编译了示例中的zpipe.c
哎呀,我得到的输出结果并不与gz兼容。
$ ./zpipe.x < data.txt > x.gz
$ file x.gz
x.gz: data
$ gunzip x.gz 
gzip: x.gz: not in gzip format

我认为这里的原因可能是因为没有调用deflateSetHeader。所以我将其添加到了我的源代码中,即(摘录,你可以在这里找到完整代码):

struct DeflateWrap { // RAII wrapper
  z_stream strm_ ; // C-Struct from zlib.h
  explicit DeflateWrap() : strm_{} {
    strm_.zalloc = Z_NULL;
    strm_.zfree = Z_NULL;
    strm_.opaque = Z_NULL;
    auto ret = deflateInit2(&strm_, LEVEL,
                 Z_DEFLATED, 15, 9, Z_DEFAULT_STRATEGY); 
    if(ret != Z_OK) throw std::runtime_error("Error ZLib-Init");
  }
  // ...more, eg. operator-> and *...
};

void pack(const string& infn) {
  DeflateWrap dwrap {};
  //...
  dwrap->avail_in = indata.size();
  dwrap->next_in = reinterpret_cast<unsigned char*>(indata.data());
  gz_header header {0}; // <<< HEADER HERE
  header.name = const_cast<unsigned char*>(
    reinterpret_cast<const unsigned char*>(infn.c_str()));
  header.comment = Z_NULL;
  header.extra = Z_NULL;
  bool first = true;
  do {
    dwrap->avail_out = outdata.size();
    dwrap->next_out = reinterpret_cast<unsigned char*>(outdata.data());
    if(first) {
      cerr << deflateSetHeader(&(dwrap.strm_), &header); // <<< SET HDR HERE
      first = false;
    }
    deflate(&(dwrap.strm_), Z_FINISH); // zlib.h: this packs
    auto toWrite = outdata.size() - dwrap->avail_out;
    outf.write(outdata.data(), toWrite);
  } while (dwrap->avail_out == 0);
}

根据我的理解,我遵循manual的指导来使用deflateSetHeader

  • 我甚至使用了deflateInit2而不是deflateInit,可能是不必要的
  • deflateSetHeader的调用紧随deflateInit2之后
  • deflateSetHeader的调用在任何deflate的调用之前

...但仍然得到了-2,即Z_STREAM_ERROR的返回值。尽管如此,我生成的输出可以通过zpipe.c进行解压缩,因此它不能完全错误,对吗?

有没有什么办法设置一个兼容gz的头文件呢?

更新:

据我所知,我使用了C++的等效函数。

SET_BINARY_MODE(stdin);
SET_BINARY_MODE(stdout);

通过这样打开文件:
ifstream inf{ infn, ifstream::binary };
ofstream outf { infn + ".gz", ofstream::binary };

此外,我想知道为什么我制作的zpipe.c示例也不能生成与gunzip兼容的文件,就像我之前描述的那样。根据我在这里阅读的内容,它应该可以。

不是重复的使用zlib创建gzip文件使用zpipe.c示例,因为我在我的C++代码中使用了二进制模式 - towi
2个回答

3
尽管我阅读了deflateSetHeader的文档,文档表明输出文件是gzip兼容的;但往后看,有一个提示可能并非如此。

该库支持使用类似于stdio的接口读写gzip(.gz)格式文件,使用以"gz"开头的函数,gzip格式与zlib格式不同。gzip是一个gzip包装器(在RFC 1952中有记录),用于包装deflate流。

因此,当我使用一组不同的gz...函数时,我获得了gzip兼容的输出和更简单的代码。
struct GzWrite { // RAII-Wrapper
    gzFile gz_ ; // C-Struct aus zlib.h
    explicit GzWrite(const string& filename)
        : gz_{gzopen(filename.c_str(),"wb9")}
    {
        if(gz_==NULL) throw std::runtime_error(strerror(errno));
    }
    ~GzWrite() {
        gzclose(gz_);
    }
    int write(const char* data, size_t len) {
        return gzwrite(gz_, data, len);
    }
    GzWrite(const GzWrite&) = delete; // keine Kopie
    GzWrite& operator=(const GzWrite&) = delete; // keine Zuweisung
};

void packe(const string& infn) {
    vector<char> indata = lese(infn); // lese Eingabe
    GzWrite gz{infn+".gz"}; // initialisiere Ausgabe
    auto res = gz.write(indata.data(), indata.size());
    if(res==0) throw std::runtime_error("Fehler beim Schreiben");
}

2

对于无压缩头和尾的原始压缩数据,windowBits也可以为-8..-15。在这种情况下,-windowBits决定了窗口大小。deflate()将生成没有zlib头或尾的原始压缩数据,并且不会计算adler32校验值。

对于可选的gzip编码,windowBits也可以大于15。将16添加到windowBits以便在压缩数据周围写入一个简单的gzip头和尾,而不是zlib包装。gzip头将没有文件名、额外数据、注释、修改时间(设置为零)、头crc,操作系统将被设置为255(未知)。如果正在写入gzip流,则strm->adler是crc32,而不是adler32。


要明确的是,这是zlib中deflateInit2()函数的重要注释,特别是关于在windowBits中添加16以获取gzip头部的部分。 - rjcarr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接