Как читать файл .gz построчно в C ++? - PullRequest
18 голосов
/ 08 июля 2010

У меня есть 3-терабайтный файл .gz, и я хочу прочитать его несжатый контент построчно в программе на C ++.Поскольку файл довольно большой, я хочу не загружать его полностью в память.

Может кто-нибудь опубликовать простой пример этого?

Ответы [ 7 ]

14 голосов
/ 08 июля 2010

Скорее всего, вам придется использовать дефлат ZLib, пример доступен на их сайте

В качестве альтернативы вы можете взглянуть на BOOST C ++ wrapper

Пример со страницы BOOST (распаковывает данные из файла и записывает их в стандартный вывод)

#include <fstream>
#include <iostream>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/zlib.hpp>

int main() 
{
    using namespace std;

    ifstream file("hello.z", ios_base::in | ios_base::binary);
    filtering_streambuf<input> in;
    in.push(zlib_decompressor());
    in.push(file);
    boost::iostreams::copy(in, cout);
}
9 голосов
/ 08 июля 2010

Для чего-то, что будет использоваться регулярно, вы, вероятно, захотите использовать одно из предыдущих предложений. Кроме того, вы можете сделать

gzcat file.gz | yourprogram

и прочитайте yourprogram из cin. Это распакует части файла в памяти по мере необходимости и отправит несжатый вывод в yourprogram.

2 голосов
/ 26 июля 2013

Используя zlib , я делаю что-то вроде этого:

// return a line in a std::vector< char >
std::vector< char > readline( gzFile f ) {
    std::vector< char > v( 256 );
    unsigned pos = 0;
    for ( ;; ) {
        if ( gzgets( f, &v[ pos ], v.size() - pos ) == 0 ) {
            // end-of-file or error
            int err;
            const char *msg = gzerror( f, &err );
            if ( err != Z_OK ) {
                // handle error
            }
            break;
        }
        unsigned read = strlen( &v[ pos ] );
        if ( v[ pos + read - 1 ] == '\n' ) {
            if ( pos + read >= 2 && v[ pos + read - 2 ] == '\r' ) {
                pos = pos + read - 2;
            } else {
                pos = pos + read - 1;
            }
            break;
        }
        if ( read == 0 || pos + read < v.size() - 1 ) {
            pos = read + pos;
            break;
        }
        pos = v.size() - 1;
        v.resize( v.size() * 2 );
    }
    v.resize( pos );
    return v;
}

РЕДАКТИРОВАТЬ: Удалил два неправильно скопированных * в примере выше. РЕДАКТИРОВАТЬ: Исправлено за пределами, считанные на v [pos + read - 2]

2 голосов
/ 08 июля 2010

Библиотека zlib поддерживает распаковку файлов в памяти в блоках, поэтому вам не нужно распаковывать весь файл для его обработки.

1 голос
/ 08 июля 2010

Вы не можете этого сделать, потому что * .gz не имеет "строк".

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

0 голосов
/ 08 июня 2015

Вот код, с помощью которого вы можете читать обычные и сжатые файлы построчно:

char line[0x10000];
FILE *infile=open_file(file);
bool gzipped=endsWith(file, ".gz");
if(gzipped) 
    init_gzip_stream(infile,&line[0]);
while (readLine(infile,line,gzipped)) {
    if(line[0]==0)continue;// skip gzip new_block
    printf(line);
}


#include <zlib.h>
#define CHUNK 0x100
#define OUT_CHUNK CHUNK*100
unsigned char gzip_in[CHUNK];
unsigned char gzip_out[OUT_CHUNK];
///* These are parameters to inflateInit2. See http://zlib.net/manual.html for the exact meanings. */
#define windowBits 15
#define ENABLE_ZLIB_GZIP 32
z_stream strm = {0};
z_stream init_gzip_stream(FILE* file,char* out){// unsigned     
        strm.zalloc = Z_NULL;
        strm.zfree = Z_NULL;
        strm.opaque = Z_NULL;
        strm.next_in = gzip_in;
        strm.avail_in = 0;
        strm.next_out = gzip_out;
        inflateInit2 (& strm, windowBits | ENABLE_ZLIB_GZIP);
    return strm;
}

bool inflate_gzip(FILE* file, z_stream strm,size_t bytes_read){
            strm.avail_in = (int)bytes_read;
            do {
                strm.avail_out = OUT_CHUNK;
                inflate (& strm, Z_NO_FLUSH);
//              printf ("%s",gzip_out);
            }while (strm.avail_out == 0);
            if (feof (file)) {
                inflateEnd (& strm);
                return false;
            }
    return true;// all OK
}


char* first_line=(char*)&gzip_out[0];
char* current_line=first_line;
char* next_line=first_line;
char hangover[1000];
bool readLine(FILE* infile,char* line,bool gzipped){
    if(!gzipped)
        return fgets(line, sizeof(line), infile) != NULL;
    else{
        bool ok=true;
        current_line=next_line;
        if(!current_line || strlen(current_line)==0 || next_line-current_line>OUT_CHUNK){
            current_line=first_line;
            size_t bytes_read = fread (gzip_in, sizeof (char), CHUNK, infile);
            ok=inflate_gzip(infile,strm,bytes_read);
            strcpy(line,hangover);
        }
        if(ok){
            next_line=strstr(current_line,"\n");
            if(next_line){
                next_line[0]=0;
                next_line++;
                strcpy(line+strlen(hangover),current_line);
                hangover[0]=0;
            }else{
                strcpy(hangover,current_line);
                line[0]=0;// skip that one!!
            }
        }
        return ok;
    }
}
0 голосов
/ 08 июля 2010

Chilkat (http://www.chilkatsoft.com/) имеет библиотеки для чтения сжатых файлов из приложений C ++, .Net, VB, ....

...