Libzip - читать содержимое файла из zip - PullRequest
5 голосов
/ 04 февраля 2012

Я использую libzip для работы с zip-файлами, и все идет хорошо, пока мне не нужно прочитать файл из zip, мне нужно прочитать только целые текстовые файлы, так что было бы здорово достичь чего-то вроде PHPФункция "file_get_contents".
Для чтения файла из zip существует функция
"int zip_fread (struct zip_file * file, void * buf, zip_uint64_t nbytes)" .
Основная проблема в том, что я не знаю, какой размер buf должен быть и сколько nbytes я должен прочитать (ну, мне нужно прочитать весь файл, но файлы имеют другой размер).Я могу просто сделать большой буфер, чтобы вместить их все и прочитать весь его размер, или сделать цикл while, пока fread не вернет -1, но я не думаю, что это рациональный вариант.

Ответы [ 4 ]

5 голосов
/ 04 февраля 2012

Вы можете попробовать использовать zip_stat, чтобы получить размер файла.http://linux.die.net/man/3/zip_stat

3 голосов
/ 04 февраля 2012

Я не использовал интерфейс libzip, но из того, что вы пишете, он выглядит очень похоже на файловый интерфейс: как только вы получили дескриптор потока, вы продолжаете вызывать zip_fread(), пока эта функция не выдаст ошибку ( возможно, меньше запрошенных байтов). Буфер, который вы передаете нам, представляет собой временный буфер разумного размера, по которому передаются данные.

Лично я бы, вероятно, создал для этого потоковый буфер, чтобы после настройки файла в zip-архиве его можно было прочитать с использованием обычных потоковых методов ввода-вывода. Это будет выглядеть примерно так:

struct zipbuf: std::streambuf {
    zipbuf(???): file_(???) {}
private:
    zip_file* file_;
    enum { s_size = 8196 };
    char buffer_[s_size];
    int underflow() {
        int rc(zip_fread(this->file_, this->buffer_, s_size));
        this->setg(this->buffer_, this->buffer_,
                        this->buffer_ + std::max(0, rc));
        return this->gptr() == this->egptr()
            ? traits_type::eof()
            : traits_type::to_int_type(*this->gptr());
    }
};

С помощью этого потокового буфера вы сможете создать std::istream и считывать файл в любую нужную вам структуру:

zipbuf buf(???);
std::istream in(&buf);
...

Очевидно, этот код не проверен и не скомпилирован. Однако, когда вы заменяете ??? на то, что необходимо для открытия zip-файла, я думаю, что это должно сработать.

2 голосов
/ 04 февраля 2012

Вот подпрограмма, которую я написал, которая извлекает данные из zip-потока и выводит строку за раз.При этом используется zlib, а не libzip, но если этот код вам полезен, не стесняйтесь его использовать:

#
# compile with -lz option in order to link in the zlib library
#

#include <zlib.h>

#define Z_CHUNK 2097152

int unzipFile(const char *fName) 
{
    z_stream zStream;
    char *zRemainderBuf = malloc(1);
    unsigned char zInBuf[Z_CHUNK];
    unsigned char zOutBuf[Z_CHUNK];
    char zLineBuf[Z_CHUNK];
    unsigned int zHave, zBufIdx, zBufOffset, zOutBufIdx;
    int zError;
    FILE *inFp = fopen(fName, "rbR");

    if (!inFp) { fprintf(stderr, "could not open file: %s\n", fName); return EXIT_FAILURE; }

    zStream.zalloc = Z_NULL;
    zStream.zfree = Z_NULL;
    zStream.opaque = Z_NULL;
    zStream.avail_in = 0;
    zStream.next_in = Z_NULL;  

    zError = inflateInit2(&zStream, (15+32)); /* cf. http://www.zlib.net/manual.html */
    if (zError != Z_OK) { fprintf(stderr, "could not initialize z-stream\n"); return EXIT_FAILURE; }

    *zRemainderBuf = '\0';
    do {
        zStream.avail_in = fread(zInBuf, 1, Z_CHUNK, inFp);
        if (zStream.avail_in == 0)
            break;
        zStream.next_in = zInBuf;
        do {
            zStream.avail_out = Z_CHUNK;
            zStream.next_out = zOutBuf;
            zError = inflate(&zStream, Z_NO_FLUSH);
            switch (zError) {
                case Z_NEED_DICT:  { fprintf(stderr, "Z-stream needs dictionary!\n"); return EXIT_FAILURE; }
                case Z_DATA_ERROR: { fprintf(stderr, "Z-stream suffered data error!\n"); return EXIT_FAILURE; }
                case Z_MEM_ERROR:  { fprintf(stderr, "Z-stream suffered memory error!\n"); return EXIT_FAILURE; }
            }
            zHave = Z_CHUNK - zStream.avail_out;
            zOutBuf[zHave] = '\0';

            /* copy remainder buffer onto line buffer, if not NULL */
            if (zRemainderBuf) {
                strncpy(zLineBuf, zRemainderBuf, strlen(zRemainderBuf));
                zBufOffset = strlen(zRemainderBuf);
            }
            else
                zBufOffset = 0;

            /* read through zOutBuf for newlines */
            for (zBufIdx = zBufOffset, zOutBufIdx = 0; zOutBufIdx < zHave; zBufIdx++, zOutBufIdx++) {
                zLineBuf[zBufIdx] = zOutBuf[zOutBufIdx];
                if (zLineBuf[zBufIdx] == '\n') {
                    zLineBuf[zBufIdx] = '\0'; 
                    zBufIdx = -1;
                    fprintf(stdout, "%s\n", zLineBuf);
                }
            }

            /* copy some of line buffer onto the remainder buffer, if there are remnants from the z-stream */
            if (strlen(zLineBuf) > 0) {
                if (strlen(zLineBuf) > strlen(zRemainderBuf)) {
                    /* to minimize the chance of doing another (expensive) malloc, we double the length of zRemainderBuf */
                    free(zRemainderBuf);
                    zRemainderBuf = malloc(strlen(zLineBuf) * 2);
                }
                strncpy(zRemainderBuf, zLineBuf, zBufIdx);
                zRemainderBuf[zBufIdx] = '\0';
            }
        } while (zStream.avail_out == 0);
    } while (zError != Z_STREAM_END);

    /* close gzip stream */
    zError = inflateEnd(&zStream);
    if (zError != Z_OK) { 
        fprintf(stderr, "could not close z-stream!\n");
        return EXIT_FAILURE;
    }
    if (zRemainderBuf)
        free(zRemainderBuf);

    fclose(inFp);

    return EXIT_SUCCESS;
}
1 голос
/ 01 марта 2015

При любой потоковой передаче вы должны учитывать требования к памяти вашего приложения.Хороший размер буфера велик, но вы не хотите использовать слишком много памяти в зависимости от требований к использованию ОЗУ.Небольшой размер буфера потребует от вас вызывать операции чтения и записи чаще, что является дорогостоящим с точки зрения производительности времени.Итак, вам нужно найти буфер в середине этих двух крайностей.

Обычно я использую размер 4096 (4 КБ), который достаточно велик для многих целей.Если вы хотите, вы можете пойти больше.Но в худшем случае размером 1 байт вы будете долго ждать завершения чтения.

Так что, чтобы ответить на ваш вопрос, «правильного» размера выбрать не нужно.Это выбор, который вы должны сделать, чтобы скорость вашего приложения и объем памяти, который вам требуется, были такими, какие вам нужны.

...