1

C ++でファイルから行ごとにデータを処理する最も効率的な(エラーが発生しにくい/一般的に「適切な」)方法は何ですか?つまり、ファイルから一度に 1 行だけを使用して、次の計算に移る前に長い計算を実行します。次のオプションを考えましたが、どれがより適切かを判断できません。

  1. 現時点では、次のようなことをしています(開いて、すべてのことを行い、最後に閉じます):

    string line; 
    fstream myfile;
    int numlines = 1000;
    myfile.open("myfile.csv");
    for(int i = 0; i < numlines; i++){
        getline(myfile, line); 
        // do something using read data
    };
    myfile.close();
    
  2. データが読み取られた直後に開いて閉じます (データのインポートよりも計算に時間がかかるため、速度はそれほど低下しません):

    string line; 
    fstream myfile;
    int numlines = 1000;
    for(int i = 0; i < numlines; i++){
        myfile.open("myfile.csv");
        for(int j = 0; j < i+1; j++)
            getline(myfile, line); 
        myfile.close();
        // do something using read data
    };
    
  3. line一度にすべてのデータを読み取ります (コンマで配列に分割されるため、~30x1000 の 2D 配列に格納する必要があります):

    string line; 
    fstream myfile;
    int numlines = 1000;
    double data[numlines][30];
    myfile.open("myfile.csv");
    for(int i = 0; i < numlines; i++){
        getline(myfile, line);
        // split by comma, store in data[][]
    } 
    myfile.close();      
    for(int i = 0; i < numlines; i++){
        // do something using data[i][]
    };
    

ここに落とし穴がありますか、または上記の解決策のいずれかが機能する場合と同じくらい優れていますか? ファイルを数時間開いたままにしておくのは良い考えではないかもしれませんが(多分?)、メモリに大きな二重2D配列を保持するのも正しくないように思えます...

4

1 に答える 1

5

できれば1を使ってください。必要に応じて 3 を使用します。2 絶対に使用しないでください。

なんで?オプション 1 は、1 つのライン バッファーのストレージのみを使用します。ファイルを 1 回だけトラバースします。開いているファイルは一般に高価なリソースではないため、最も安価で単純なものである可能性があります。

ただし、オプション 1 が常に適切であるとは限りません。行をランダムな順序で処理する必要がある場合があります。ここでは、オプション 3 が最適です。この場合、十分なメモリがあれば、ファイル全体を読み取って内容をメモリに抽出するのが最も簡単です。多くの場合、文字列の配列で十分です。あなたのものでは、行に double のテキスト表現が含まれているようです。したがって、読みながらこれらを抽出するのが適切です。一般に、ストレージおよび/またはアクセス効率の高い形式で抽出する必要があります。

ファイルが大きすぎて内容がメモリに収まらない場合は、ランダム ファイル アクセス (fseekまたはseekC++) を使用する必要があります。テキスト行の場合は、それを読んで行頭のオフセットを見つけます。これらを配列に格納して、行インデックスとして機能させます。適切なインデックス エントリを使用して行の開始をシークすることにより、行にアクセスします。次に、次の改行まで読み取ります。インデックスは、1 行あたり 8 バイトに加えて、1 行分のバッファーになります。ファイルが非常に大きい場合は、インデックスをファイルに保存して、1 行のアクセスごとに 2 回シークできます。インデックスとデータを別のディスク ドライブに配置して、シーク時間を短縮することをお勧めします。インデックスを削除するもう 1 つのオプションは、すべての行の長さが同じであることを要求することです。これにより、任意の行を見つけるのに算術演算で十分です。

オプション 2 は、1 行の処理中に 1 つの開いているファイルを維持することで過度のコストがかかる場合にのみ意味があります。これは事実上決してそうではありません。コードは、n 単位のファイルに対して O(n^2) 単位のデータを読み取る必要があります。問題が大きくなるにつれて、パフォーマンスが非常に悪くなります。ファイル IO はプログラムのボトルネックになることが多いため、これは非常に悪いことです。

さらに、ファイルのオープンとクローズはかなり高価な操作であり、勝手に行うべきではありません。以前、大規模なシミュレーション システムに取り組んでいたときに、それを高速化できるかどうかを尋ねられました。確かに、それが何をしていたかを考えると、それは過度に遅いように見えました。コードのリバース エンジニアリングを数週間行った後、最終的に、トレース ファイルが追加のために開かれ、イベント ループの反復ごとに 1 回閉じられていることがわかりました。オープンとクローズをループの外側に移動し (ループの内側に時折フラッシュを追加して置き換えます)、そしてワフー! シミュレーションは 20 倍以上高速化されました。クライアントは控えめに言っても幸せでした。

于 2013-05-07T03:27:17.970 に答える