すべてのカテゴリ » インターネット・パソコン » 技術・プログラミング

質問

終了

CSVファイルから、AND条件を満たす行の値を検索したいです。
行数:300万行(ファイルサイズは、30MB)
列数:9列
検索条件:2行目の値と3行目の値にマッチするもの(AND条件)
検索データ:該当行の9列目のデータ
※2行目のデータでソート済みのデータです。

メモリに2、3、9行目を全て配列に読み込んで、二分探索するようにコーディングしてみたのですが、配列に読み込む部分の処理が遅いです(5、6秒)。
他に高速な方法はないでしょうか?

  • 質問者:ss
  • 質問日時:2009-03-28 19:09:35
  • 0

並び替え:

質問を読む限り、行と列の概念が入り乱れている様に思います。
具体的に何をやろうとしているのか、こちらで勝手に整理したので間違っていたら指摘してください。

【やりたい事】
あるファイルから条件に該当するデータを検索する。

【対象ファイル】
300万行9列のCSVファイル(約30MB)。
ファイルフォーマット例
1-1,1-2,1-3,1-4,1-5,1-6,1-7,1-8,1-9
2-1,2-2,2-3,2-4,2-5,2-6,2-7,2-8,2-9
3-1,3-2,3-3,3-4,3-5,3-6,3-7,3-8,3-9
4-1,4-2,4-3,4-4,4-5,4-6,4-7,4-8,4-9
5-1,5-2,5-3,5-4,5-5,5-6,5-7,5-8,5-9
 :
 :
300万-1,300万-2,300万-3,300万-4,300万-5,300万-6,300万-7,300万-8,300万-9

【検索条件】
2列目の値=A かつ 3列目の値=B(A、Bは仮の値)

【取得するデータ】
検索条件に合致する行の9列目の値。
ファイルフォーマット例で、検索条件をA=2-2、B=2-3とした場合、結果として2-9が取得される。

【問題点】
データを配列に読み込む処理が遅い。


もしも上記の内容で合っているならば、正規表現を利用すれば良いと思います。
まずCSVデータを1つ1つ配列に入れる事をやめます。
そして、
 1.CSVデータを1つの文字列として読み込む。
 2.正規表現で条件を指定。
 3.条件にヒットした行をピックアップ。
 4.ピックアップした行を配列に落とし、9列目のみを取得。
とします。
今回扱うのはCSVファイルなので、検索条件を"^\(.*,\)\{1\}A,B\(,.*\)\{6\}$"とすれば、条件に合致する行だけを取得する事は可能です。

OSがUNIXでgrepとsedがあるなら、
 grep '^\(.*,\)\{1\}A,B\(,.*\)\{6\}$' target.csv | sed -e "s/^.*,//"
の1行で、条件に合う行の9列目だけを取得することができます。
私のところで300万行のデータ(ファイルサイズ180MB程)を作ってこのコマンドを試しましたが、処理に1秒もかからなかったです。

===補足===
Regex regex = new Regex("A,B");
にするとの事ですが、これだと正しく動きません。
"A,B"という組み合わせが他の列で出てきてしまえば、間違ったデータまでヒットしてしまいます。

例えば、以下の様なレコードが引っかかります。
(このうち本当に欲しいレコードは1つも無いはず)
A,B,1-3,1-4,1-5,1-6,1-7,1-8,1-9
2-1,2-2,A,B,2-5,2-6,2-7,2-8,2-9
3-1,3-2,3-3,A,B,3-6,3-7,3-8,3-9
4-1,4-2,4-3,4-4,A,B,4-7,4-8,4-9
5-1,5-2,5-3,5-4,5-5,A,B,5-8,5-9
6-1,6-2,6-3,6-4,6-5,6-6,A,B,6-9
7-1,7-2,7-3,7-4,7-5,7-6,7-7,A,B

この様な不具合を避けるためにも、検索条件は正確にした方が良いと思います。

  • 回答者:ジョン (質問から3時間後)
  • 1
この回答の満足度
  
とても参考になり、非常に満足しました。回答ありがとうございました。
お礼コメント

丁寧にご説明していただきありがとうございます。
おかげさまで大分早くなりました。

ただ、CSVデータを1つの文字列として読み込むと、行頭を表す ^ は文字列の最初(1行目の行頭)にしかマッチしないようで、
"^\(.*,\)\{1\}A,B\(,.*\)\{6\}$" ではなく、
"\r\n(.*,){1}A,B(,.*){6}\r\n"
としたらマッチしました。しかし、これだと5秒半かかってしまいました。
 遅いので以下の2つの条件で1行ずつ文字列lineに読み込んでみたところ、
条件1.Regex regex = new Regex("^(.*,){1}A,B(,.*){4},(.*)(,.*){3}$");
条件2.Regex regex = new Regex("A,B");
条件1.の結果は、5秒くらい、
条件2.の結果は、0.9秒くらい
でした。条件2でいこうと思います。
Windowsだとgrepの機能が遅いのでしょうか・・
いずれにせよ、検索するときは全てメモリに読み込まなければならないものだとばかり思っていたので、本当に助かりました。ありがとうございました。

関連する質問・相談

Sooda!からのお知らせ

一覧を見る