Как создать 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");
}