使用 C++ 检索一些文本列比 python 慢得多

retrieve some text columns using c++ much slower than python

我想读取一些大文件内容并检查一些列,然后根据示例行的列值存储一些文件行:

7774777761 72288833         2015/03/20     23:59:37       26       26   38 
  99944524 09671017         2015/03/20     23:59:44       18        1    8

我在 Python 中是这样做的:

import sys
if __name__=="__main__":
    if (len(sys.argv)<4):
        sys.stderr.write('Usage: trk finame fout  column value \n ')
        sys.exit(1)
    finame=open(sys.argv[1],'r')
    result=open(sys.argv[2],'w')
    nos=open("nos.txt",'w')
    col=int(sys.argv[3])
    val=sys.argv[4]
    for l in finame:
        llist=l.split()
        try:
            if llist[col]==val:
                result.write(l)
        except:
            nos.write(l)
    result.close()
    nos.close()

然后尝试使用正则表达式在 C++ 中执行此操作:

#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <regex>
using namespace std;

int main(int argc, char* argv[])
{
  ifstream fstr;
  ofstream ofstr;
  string istr,result;
  int col;
  string val;
  if(argc<5){
    cout<<"you must enter right arguments"<<endl;
    cout<<"colgrab inputfile outputfile desired_col desired_val"<<endl;
    cout<<"for example :"<<endl;
    cout<<"colgrab TrkTicket.txt INCOM_HWI.txt 6 1"<<endl;
  }else{
    fstr.open(argv[1]);
    ofstr.open(argv[2]);
    col=atoi(argv[3]);
    val=argv[4];
    if(!fstr)
    {
      cerr << "File could not be opened" << endl;
      exit( 1 );
    }

    if(!ofstr)
    {
      cerr << "File could not be opened" << endl;
      exit( 1 );
    }
  }

  while(getline(fstr,istr)){
    //  cout<<istr<<endl;
    try {
      regex re(R"XXX( *(\d+) +(\d+) +(\d+/\d+/\d+) +(\d+:\d+:\d+) +(\d+) +(\d+) +(\d+).)XXX");
      std::smatch match;
      //cout<<istr<<endl;
      if (regex_search(istr, match, re) && match.size() > 1) {
        result = match.str(col);

        if(val==result){
          ofstr<<istr<<endl;
        }
        //cout<<result<<endl;
      } else {
        //result = std::string("No match found");
        //cout<<result<<endl;

      }
    } catch (std::regex_error& e) {
      // Syntax error in the regular expression
      //cerr<<"Syntax error in the regular expression "<<endl;
    }
  }


  return 0;
}

我这样做的目的是速度。但令我惊讶的是,Python 版本在不到 10 秒的时间内完成了一个 270 Mb 文件的工作,但 C++ 版本无法在 10 分钟内完成工作。

如何修复 C++ 版本以在更短的时间内完成这项工作?


Python版本python3.2

C++ 版本 GCC G++ 4.9.1


编辑 1

我尝试了所有建议的方法,使用 MikeMB 的方法它们几乎相等:

#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <regex>
using namespace std;

int main(int argc, char* argv[])
{
    ifstream fstr;
    ofstream ofstr;
    string istr,result;
    int col;
    string val;
    if(argc<5){
        cout<<"you must enter right arguments"<<endl;
        cout<<"colgrab inputfile outputfile desired_col desired_val"<<endl;
        cout<<"for example :"<<endl;
        cout<<"colgrab TrkTicket.txt INCOM_HWI.txt 6 1"<<endl;
    }else{
    fstr.open(argv[1]);
    ofstr.open(argv[2]);
    col=atoi(argv[3]);
    val=argv[4];
    if(!fstr)
       {
          cerr << "File could not be opened" << endl;
          exit( 1 );
       }

    if(!ofstr)
       {
          cerr << "File could not be opened" << endl;
          exit( 1 );
       }
    }

while(getline(fstr,istr)){
        stringstream sstr(istr);
        int i = 0;
        while (sstr >> result) {
           if (i == col-1 && result == val) {
               ofstr << istr << "\n";
               break;
           }
           i++;
        }
 


    return 0;
}

有没有办法进一步提高性能?

为了完全删除regex,你可以试试这个(只修改了while循环):

while(getline(fstr,istr)){
    stringstream sstr(istr);
    string field[7];
    for(int i = 0; i < 7; i++)
        sstr >> field[i];

    // do whatever you want with read values
}

假设您阅读的每一行都有 7 列并且列中的值不包含空格,这应该可以工作。

构建正则表达式非常昂贵,因为它涉及构建状态机,因此,如评论中所述,您应该将正则表达式构建移出循环,这样您只需付出一次代价。

但是,对于这种简单的情况,您可能根本不需要正则表达式。我不确定这是否真的更快,但您可以尝试以下操作:

while (getline(fstr, istr)){        
    std::stringstream ss(istr);
    int i = 0;      
    while (ss >> result) {              
        if (i == col && result == val) {
            ofstr << istr << "\n";
            break;
        }
        i++;
    }
}

编辑 1 的答案:

  • 添加 std::ios::sync_with_stdio(false); 作为 main() 的第一行 不错的速度提升。

  • 你不需要读一行然后把它转换成stringstream - 您可以直接从 fstr 读取值以避免复制。

  • 要在毫秒内获取数据,您可以使用索引数据格式,例如将数据导入 SQLite database,索引列并使用数据库查询来提取它。