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

How to create a gz-compatible file with zlib?

本文关键字:gz 文件 一个 zlib 何使用 创建      更新时间:2023-10-16

我想使用zlib用c++生成一个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);
}

对于我的解释,我遵循deflateSetHeader的手册:

  • 我甚至用deflateInit2代替deflateInit,可能没有必要
  • deflateSetHeader的调用紧接deflateInit2
  • deflateSetHeader的调用先于deflate的调用

…我仍然从deflateSetHeader调用中得到-2,即Z_STREAM_ERROR。虽然,我产生的输出可以用zpipe.c解压缩,因此它不可能完全错误,可以吗?

任何想法如何设置一个gz兼容的标题?

更新:

正如我所看到的,我使用c++ -pendant来

SET_BINARY_MODE(stdin);
SET_BINARY_MODE(stdout);

:

ifstream inf{ infn, ifstream::binary };
ofstream outf { infn + ".gz", ofstream::binary };

另外,我想知道为什么我制作的zpipe.c示例也没有像我之前描述的那样制作一个与zip兼容的文件。从我在这里读到的应该是

虽然我读了deflateSetHeader的文档,输出文件 gz兼容的,但再往下看,有一个提示,它可能不是这样的。

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

因此,当我使用不同的函数集gz...时,我得到与gz兼容的输出更简单的代码:
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");
}

windowsbits也可以是-8 ..-15为原始放气。在这种情况下,-windowBits决定窗口大小。然后Deflate()将生成原始的Deflate数据,不带zlib头或尾,并且不会计算adler32检查值。

对于可选的gzip编码,

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