【问题标题】:How to read a .gz file line-by-line in C++?如何在 C++ 中逐行读取 .gz 文件?
【发布时间】:2010-07-08 07:42:20
【问题描述】:

我有 3 TB 的 .gz 文件,想在 C++ 程序中逐行读取其未压缩的内容。由于文件非常大,我想避免将其完全加载到内存中。

任何人都可以发布一个简单的例子吗?

【问题讨论】:

  • 必须解压缩它才能阅读。但是,您可以将其解压缩到内存中,而不是在磁盘上。你是这个意思吗?
  • 你不能 - 没有要阅读的行。
  • 什么是 3T? @Neil 是对的,.gz 文件没有“行”,它是二进制格式。
  • @TJB - 很可能意味着 3 TB
  • @TJB:我猜是 3 TB。因此无法解压整个文件。

标签: c++ compression gzip


【解决方案1】:

您很可能必须使用 ZLib 的 deflate,示例可从他们的 site 获得

您也可以查看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);
}

【讨论】:

【解决方案2】:

对于要经常使用的东西,您可能想要使用前面的建议之一。或者,你可以这样做

gzcat file.gz | yourprogram

并让 yourprogram 从 cin 读取。这将根据需要解压缩内存中的部分文件,并将未压缩的输出发送到yourprogram

【讨论】:

    【解决方案3】:

    使用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] 上的越界读取

    【讨论】:

    • 嗯,这至少被否决了两次。这是一个关于如何使用“基本”zlib 恕我直言的工作示例,因此请在投票时考虑评论以使改进成为可能。
    • 当行只有一个字符 '\n' (在 linux 中作为空行)时,这在 v[pos+read-2] 上有一个重大错误,你有一个超出数组读取(-1被转换为一个大的无符号内存地址)。我已经添加了预检查。
    【解决方案4】:

    zlib 库支持以块为单位解压缩内存中的文件,因此您不必为了处理它而解压缩整个文件。

    【讨论】:

      【解决方案5】:

      这里有一些代码,您可以使用它们逐行读取普通文件和压缩文件:

      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;
          }
      }
      

      【讨论】:

      • 您能否将一个完整的工作示例上传到 onlinegdb.com 与 .gzip 一起使用?
      • 您能否上传一个完整的工作示例(读取 .gz 文件并逐行输出文本)?谢谢
      【解决方案6】:

      你不能这样做,因为 *.gz 没有“行”。

      如果压缩数据有换行符,则必须对其进行解压缩。您不必一次解压缩所有数据,您知道,您可以分块进行,并在遇到换行符时将字符串发送回主程序。 *.gz 可以使用zlib解压。

      【讨论】:

        【解决方案7】:

        Chilkat (http://www.chilkatsoft.com/) 具有从 C++、.Net、VB 等应用程序读取压缩文件的库。

        【讨论】:

          猜你喜欢
          • 2019-05-29
          • 1970-01-01
          • 1970-01-01
          • 2022-01-25
          • 1970-01-01
          • 2010-12-24
          • 2010-11-19
          • 1970-01-01
          相关资源
          最近更新 更多