使用gzbuffer快速读取GZ的文件,然后按行划分内容

Use gzbuffer to read gzipped file quickly and then split content line by line

本文关键字:然后 划分 文件 gzbuffer 读取 GZ 使用      更新时间:2023-10-16

我想要一个作为输入文件名和字符串向量的函数,并且通过有效读取文件来填充矢量。这是我到目前为止所做的:

/** brief Read the whole file in a vector of lines
 */
  int
  readFile(
    const string & pathToFile,
    vector<string> & lines)
  {
    gzFile stream;
    openFile(pathToFile, stream, "rb");
    int errnum;
    const char * error_msg = NULL;
    size_t nb_bytes_to_read = 256000; // 8192 is default for gzbuffer
    if(gzbuffer(stream, nb_bytes_to_read) == -1){
      error_msg = gzerror(stream, &errnum);
      if(errnum != Z_OK){
        cerr << "ERROR: gzbuffer failed with " << nb_bytes_to_read
             << " bytes" << endl;
        cerr << error_msg << endl;
        exit(EXIT_FAILURE);
      }
    }
    size_t buf_len = nb_bytes_to_read;
    char * buf = (char *) malloc(buf_len);
    if(buf == NULL){
      cerr << "ERROR: can't allocate " << nb_bytes_to_read
           << " bytes" << endl;
      exit(EXIT_FAILURE);
    }
    size_t nb_bytes_read = 0, tot_nb_bytes_read = 0;
    while(! gzeof(stream)){
      nb_bytes_read = gzread(stream, buf + tot_nb_bytes_read,
                             nb_bytes_to_read);
      tot_nb_bytes_read += nb_bytes_read;
      if(nb_bytes_read < nb_bytes_to_read && ! gzeof(stream)){
        error_msg = gzerror(stream, &errnum);
        if(errnum != Z_OK){
          cerr << "ERROR: gzread failed on " << pathToFile << endl;
          cerr << error_msg << endl;
          exit(EXIT_FAILURE);
        }
      }
      if(tot_nb_bytes_read == buf_len){
        buf_len += nb_bytes_to_read;
        buf = (char*) realloc(buf, buf_len);
        if(buf == NULL){
          cerr << "ERROR: can't allocate " << nb_bytes_to_read
               << " bytes" << endl;
          exit(EXIT_FAILURE);
        }
      }
    }
    closeFile(pathToFile, stream);
    lines = split(buf, "n", lines);
    free(buf);
    return 0;
  }

gzread的Zlib文档提到:"如果Gzip流以Gzip流遇到了GZIP流以外的其他内容,则忽略了剩下的落后垃圾(不会返回错误)"。但是,对于某些文件,我上面的代码读为"一行太远"。更具体地说,输出"线"向量具有N元素,而输入文件具有N-1行。结果,"线"向量的最后一个元素可以是" 223(305ŀV"。

我该如何解决?

这是上面代码中使用的其他功能:

  void
  openFile(
    const string & pathToFile,
    gzFile & fileStream,
    const char * mode)
  {
    fileStream = gzopen(pathToFile.c_str(), mode);
    if(fileStream == NULL){
      cerr << "ERROR: can't open file " << pathToFile
           << " with mode " << *mode
           << " (errno=" << errno << ")" << endl;
      exit(EXIT_FAILURE);
    }
  }
  void
  closeFile(
    const string & pathToFile,
    gzFile & fileStream)
  {
    int ret = gzclose(fileStream);
    if(ret != Z_OK){
      cerr << "ERROR: can't close the file " << pathToFile
           << ", gzclose() returned " << ret << endl;
      exit(EXIT_FAILURE);
    }
  }
  vector<string> &
  split(
    char * buf,
    const char * delim,
    vector<string> & tokens)
  {
    tokens.clear();
    char * pch;
    pch = strtok(buf, delim);
    while(pch != NULL){
      tokens.push_back(string(pch));
      pch = strtok(NULL, delim);
    }
    return tokens;
  }

(由于我不是专业程序员,欢迎其他建议!)

strtok()在null终止的字符串上操作。您正在提供一个缓冲区,大概是文本文件。没有空。因此,strtok()正在读取缓冲区的末端,直到在内存中发现意外零为止。

顺便说一句,strtok()有问题,甚至没有再入侵。阅读strtokstrsep的男人页面。