Libz не извлекает весь файл gzip

Это (1,4M) можно без проблем распаковать с помощью gzip

gzip --stdout --decompress ./archiveteam_blip_20150813160102.cdx.gz

Он распаковывает до 61400 строк текста.

Однако с этим кодом, основанным на zpipe.c, примере сценария, приведенном на веб-сайте libz (запуская с помощью ./zpipe archiveteam_blip_20150813160102.cdx.gz, я получаю только 3000 строк данных:

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "zlib.h"

#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
#  include <fcntl.h>
#  include <io.h>
#  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
#else
#  define SET_BINARY_MODE(file)
#endif

#define CHUNK 16384

int inf(FILE *source, FILE *dest)
{
    int ret;
    unsigned have;
    z_stream strm;
    unsigned char in[CHUNK];
    unsigned char out[CHUNK];

    /* allocate inflate state */
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.avail_in = 0;
    strm.next_in = Z_NULL;
    ret = inflateInit2(&strm, 16 + MAX_WBITS);
    if (ret != Z_OK)
        return ret;

    /* decompress until deflate stream ends or end of file */
    do {
        strm.avail_in = fread(in, 1, CHUNK, source);
        if (ferror(source)) {
            (void)inflateEnd(&strm);
            return Z_ERRNO;
        }
        if (strm.avail_in == 0)
            break;
        strm.next_in = in;

        /* run inflate() on input until output buffer not full */
        do {
            strm.avail_out = CHUNK;
            strm.next_out = out;
            ret = inflate(&strm, Z_NO_FLUSH);
            assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
            switch (ret) {
            case Z_NEED_DICT:
                ret = Z_DATA_ERROR;     /* and fall through */
            case Z_DATA_ERROR:
            case Z_MEM_ERROR:
                (void)inflateEnd(&strm);
                return ret;
            }
            have = CHUNK - strm.avail_out;
            if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
                (void)inflateEnd(&strm);
                return Z_ERRNO;
            }
        } while (strm.avail_out == 0);

        /* done when inflate() says it's done */
    } while (ret != Z_STREAM_END);

    /* clean up and return */
    (void)inflateEnd(&strm);
    return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}

/* compress or decompress from stdin to stdout */
int main(int argc, char **argv)
{
    int ret;

    FILE *source = fopen(argv[1], "r");

    /* avoid end-of-line conversions */
    SET_BINARY_MODE(source);
    SET_BINARY_MODE(stdout);
    
    ret = inf(source, stdout);
    
    if (ret != Z_OK)
    {
        fputs("zpipe: ", stderr);
        switch (ret) {
        case Z_ERRNO:
            if (ferror(stdin))
                fputs("error reading stdin\n", stderr);
            if (ferror(stdout))
                fputs("error writing stdout\n", stderr);
            break;
        case Z_STREAM_ERROR:
            fputs("invalid compression level\n", stderr);
            break;
        case Z_DATA_ERROR:
            fputs("invalid or incomplete deflate data\n", stderr);
            break;
        case Z_MEM_ERROR:
            fputs("out of memory\n", stderr);
            break;
        case Z_VERSION_ERROR:
            fputs("zlib version mismatch!\n", stderr);
        }
    }
    
    return ret;
}

Формат Gzip отличается от формата zlib. zlib поддерживает оба варианта, но с ними нужно обращаться немного по-другому.

John Bollinger 29.07.2024 16:18

@JohnBollinger Следуя руководству, я попытался вызвать inflateInit2 и передать 16 в качестве второго аргумента, но теперь у меня возникла другая проблема. Сценарий завершается нормально, но я получаю только 3000 строк распакованных данных. Фактическое содержимое при распаковке с помощью gzip составляет около 64 тыс. строк.

Jack M 29.07.2024 16:32

Если ответы на цель обмана не решают вашу проблему, вам следует рассмотреть возможность редактирования этого вопроса, чтобы сосредоточиться на проблеме, описанной в вашем комментарии, включая минимальный воспроизводимый пример для этого, а затем запросить повторное открытие. Альтернативно, вы можете опубликовать новый вопрос в том же духе.

John Bollinger 29.07.2024 16:45

В документации написано «добавить». Используйте 31 (15+16) с inflateInit2(), чтобы распаковать формат gzip, или 47 (15+32), чтобы декодировать формат zlib или gzip, автоматически определяя, какой это формат.

Mark Adler 29.07.2024 16:55

Возможно ли, что файл .gz содержит несколько объединенных потоков?

Ian Abbott 29.07.2024 18:25

@IanAbbott Действительно. 21 из них.

Mark Adler 29.07.2024 19:24
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваш файл gzip имеет несколько членов gzip. Согласно спецификации, объединение любого количества потоков gzip также является допустимым потоком gzip. Из документации в zlib.h:

В отличие от утилиты Gunzip и gzread() (см. ниже), inflate() не будет автоматически декодировать объединенные элементы gzip. inflate() вернет Z_STREAM_END в конце элемента gzip. Состояние необходимо будет сбросить, чтобы продолжить декодирование последующего элемента gzip. Это необходимо сделать, если после элемента gzip имеется больше данных, чтобы распаковка соответствовала стандарту gzip (RFC 1952).

Получив Z_STREAM_END, вам нужно посмотреть, остались ли у вас какие-либо данные (strm.avail_in != 0) или можете ли вы прочитать дополнительные данные. Если да, вам нужно выполнить inflateReset() и декодировать следующий элемент gzip, а затем продолжить цикл do.

Ах я вижу! Я дважды пытался вызвать функцию inf, но думаю, что во входном буфере остались данные, и поэтому она не работала. В любом случае, я тоже только косвенно узнал, что gzopen и gzread существуют.

Jack M 29.07.2024 19:44

Другие вопросы по теме