2017-07-16 1 views
1

私はC++で解析する必要がある〜3MBのテキストファイルがいくつかあります。3MBのテキストファイルを効率的に読み込みます。構文解析

テキストファイルは、この(1024x786)のようになります。

12,23 45,78 90,12 34,56 78,90 ... 
12,23 45,78 90,12 34,56 78,90 ... 
12,23 45,78 90,12 34,56 78,90 ... 
12,23 45,78 90,12 34,56 78,90 ... 
12,23 45,78 90,12 34,56 78,90 ... 

手段Tabで区切られた「ナンバーブロック」、および数字自体は,を含む小数のマーカー(.のinsted)。

まず、ファイルを読む必要があります。 「ナンバーブロック」が、私は私を得るという点で素敵働いている

#include <boost/tokenizer.hpp> 

string line; 
ifstream myfile(file); 
if (myfile.is_open()) 
{ 
    char_separator<char> sep("\t"); 
    tokenizer<char_separator<char>> tokens(line, sep); 
} 
myfile.close(); 

はまだフロートにこのcharを変換する必要がありますが、小数点マーカーとして,の取り扱い:現在、私はこれを使用しています。ファイルサイズのため、私はそれも良い考えではないと思いますtokenizeこれも同様です。さらに、この値をすべて後で場所別にアクセスできるデータ構造に追加する必要があります(例:[x][y])。どのようにこれを実現するためのアイデア?私は前方にまっすぐにどうなるのか

+1

これは3メガバイトのファイルについては本当にあるならば、私はちょうどそれをメモリにロードすると思います。これは、ほとんどのマシンで利用可能なメモリに比べてそれほど大きくはありません。 –

+1

メモリマップファイル(boost.iostreams)と簡単なboost.spiritパーサー。すべてのファイルをロードするスレッドプール。おそらく、過度なものですが、速くしたいのであれば... –

答えて

2

あなたはファイルの内容を解析するBoost.Spiritを使用することができますし、最終的な結果として、あなたは、パーサーからデータを得ることができますたとえばstd::vector<std::vector<float>>のように構造化されています。 IMO、あなたの共通ファイルのサイズは大きくありません。ファイル全体をメモリに読み込んでパーサを実行する方が良いと思います。ファイルを読み取るための効率的なソリューションは、read_fileに示されています。

は、長さとサイズがfloatタイプによって制限された実数を解析し、.(ドット)をセパレータとして使用します。区切り記号はqi::real_policies<T>::parse_dotでカスタマイズできます。以下、spirit/example/qi/german_floating_point.cppのコードスニペットを使用しています。

はこのデモを見てみましょう:

#include <boost/spirit/include/qi.hpp> 
#include <fstream> 
#include <iostream> 
#include <string> 
#include <vector> 

std::string read_file(std::string path) 
{ 
    std::string str; 
    std::ifstream file(path, std::ios::ate); 
    if (!file) return str; 
    auto size(file.tellg()); 
    str.resize(size); 
    file.seekg(0, std::ios::beg); 
    file.rdbuf()->sgetn(&str[0], size); 
    return str; 
} 

using namespace boost::spirit; 

//From Boost.Spirit example `qi/german_floating_point.cpp` 
//Begin 
template <typename T> 
struct german_real_policies : qi::real_policies<T> 
{ 
    template <typename Iterator> 
    static bool parse_dot(Iterator& first, Iterator const& last) 
    { 
     if (first == last || *first != ',') 
      return false; 
     ++first; 
     return true; 
    } 
}; 

qi::real_parser<float, german_real_policies<float> > const german_float; 
//End 

int main() 
{ 
    std::string in(read_file("input")); 
    std::vector<std::vector<float>> out; 
    auto ret = qi::phrase_parse(in.begin(), in.end(), 
           +(+(german_float - qi::eol) >> qi::eol), 
           boost::spirit::ascii::blank_type{}, 
           out); 
    if(ret && in.begin() == in.end()) 
     std::cout << "Success" << std::endl; 
} 
+1

はい。ここのベンチマーク(QiとX3の両方)https://stackoverflow.com/questions/17465061/how-to-parse-space-separated-floats-in-c-quickly/17479702#17479702を参照してください。 [比較のためのこれらのベンチマーク](https://stackoverflow.com/questions/26736742/efficiently-reading-a-very-large-text-file-in-c/26737146#26737146)も参照してください。 – sehe

0

boost::tokenizerが不要で、すべて):

std::setlocale(LC_NUMERIC, "de_DE"); // Use ',' as decimal point 
std::vector<std::vector<double>> dblmat; 
std::string line; 
while(std::getline(myfile,line)) { 
    dblmat.push_back(std::vector<double>()); 
    std::istringstream iss(line); 
    double val; 
    while(iss >> val) { 
     dblmat.back().push_back(val); 
    } 
} 
+1

各列に「istringstream」を再現することは安くはありません。また、グローバルロケールをファイルを読み込むように設定することは悪い考えです(特にロケールを安全に設定できないマルチスレッドプログラムでは)。私は、ファイルストリーム内の正しいロケールを 'ただ'即時に 'imbue'して、新しい行を開始するか、各抽出後にどの区切り文字が見つかるかに基づいて最後の行に追加するかどうかを決定します。 –

+0

@Matteo私は最適化の余地があると確信しています。私は 'boost :: tokenizer'や他の洗練された解析ツールを使わずに、OPが望むものをやるための簡単な方法があることを指摘したかっただけです。 – user0042

関連する問題