Как создать gz-совместимый файл с zlib?

Я хочу использовать zlib для создания gz-совместимого выходного файла с C++.

Я установил пакет разработчика для zlib, который, как я понимаю, можно использовать для создания gz-совместимых файлов как в Unix, так и в Windows.

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

... и все же я получаю -2т.е. Z_STREAM_ERROR от deflateSetHeader вызов. Хотя вывод, который я создаю, может быть распакован 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 файл, как я описал ранее. Из того, что я прочитал здесь, это должно быть.

2 ответа

Решение

WindowBits также может быть –8..–15 для необработанного дефлята. В этом случае -windowBits определяет размер окна. Затем deflate() сгенерирует необработанные данные deflate без заголовка или трейлера zlib и не вычислит значение проверки adler32.

WindowBits также может быть больше 15 для необязательного кодирования gzip. Добавьте 16 к windowBits, чтобы написать простой заголовок gzip и трейлер вокруг сжатых данных вместо оболочки zlib. В заголовке gzip не будет ни имени файла, ни дополнительных данных, ни комментариев, ни времени изменения (установленного в ноль), ни заголовка crc, а для операционной системы будет установлено значение 255 (неизвестно). Если пишется поток gzip, strm->adler - это crc32 вместо adler32.

Хотя я читаю документацию deflateSetHeader что выходной файл gz-совместим, чуть ниже есть подсказка, что это может быть не так.

Эта библиотека поддерживает чтение и запись файлов в формате gzip (.gz) с интерфейсом, аналогичным интерфейсу stdio, с использованием функций, начинающихся с "gz". Формат gzip отличается от формата zlib. gzip - это оболочка gzip, задокументированная в RFC 1952, обернутая вокруг потока дефлятов.

Таким образом, когда я использую другой набор функций 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");
}
Другие вопросы по тегам