• 締切済み

文字列の検索高速化について

大容量の文字列から文字列を検索するのに四苦八苦しております。 現在は文字列を構造体→メモリを確保でなんとかやっておりますが、データの容量が大きい為、時間が掛かりすぎてしまいます。 データ 70文字×30000行程度(行数可変)の文字列ファイル(すべて半角英数) 検索する側、検索される側共に同じファイルを使用します。 検索する側から一行ずつ文字列を取得→特定のカラムから文字列を抜き出す→検索される側からヒットした行を抜き出す→別処理を行う (検索に使用した行はスルー、検索される側には検索文字列が複数存在する可能性あり→すべて抜き出したい) また、抜き出した行は検索する側、される側共に次以降二重処理して速度が遅くなってしまうかと思うので、できれば削除したい。(現在は二重処理も致仕方ないとしてそのままやっています) 過去ログを読むとハッシュテーブルが良いかと言われていますが、このような場合はどのようなアルゴリズムが良いのでしょうか。 質問がわかりずらく申し訳ないですが、アドバイスを頂ければと思います。 また、使用方法等が詳しく記載されているページ等がありましたら教えて頂ければ幸いです。

みんなの回答

  • fonera
  • ベストアンサー率52% (38/72)
回答No.4

恐れ入ります。 データサイズに変更がない場合は、固定長のメモリを確保するのが良いと思います。 今回のデータが、「常に3万行」なのか「3万行程度」なのか判りませんので、動的メモリ割当を想定しました。 >p = 新しくメモリを確保する; >↑この場合どのようにメモリを確保したらよいでしょうか。 >(mallocを使用するのでしょうか?) はい。mallocを使います。通常は以下のように書くと思います。 p = (struct node *) malloc(sizeof(struct node)); >for (ファイルの終わりまで) { >要素を1つ読み出す; >element = create_hash(element, 読み出した要素, 読み出した行); >} > >↑ここをもう少し詳しくお願いできませんでしょうか。 >(日本語部分) できません。データの形式が不明だからです。 # No.1の方の補足にソースを開示しておられますが、char[75]を抱える単方向リンクリストにしか見えません。 # 単方向リンクリストは挿入削除が高速なのが利点で、検索の高速性とは無関係な為、今回の質問と関連があるか判らない。 データの形式が不明な為、回答できません。 ・カンマ区切りなのか、タブ区切りなのか、ハイフン区切りなのか ・リンクリストに入れて、他に処理を行うのか ・質問に書かれていない処理が別途入り、検索自体は数値で行っているのか ・メモリをどの程度まで使用して良いのか なにをするどんなプログラムなのかを詳らかにしていただくか、 一切手を加えてないデータ(ダミーデータでも形式・数値範囲があっているもの)と 検索過程にに入る処理API全てをお教え願えないと、回答が難しいです。 # アルゴリズムでなく、コーディングレベルの回答をしなければならない為。 # (個人的には)要素を単純に一つづつ読み出しているだけなので、 # 現在お使いのソースを変更するだけで対処できるものかとは思いますが・・・ 以上、ご参考になれば幸いです。

  • fonera
  • ベストアンサー率52% (38/72)
回答No.3

恐れ入ります。 条件が明確でないため、参考になるか判りませんが、現時点での情報から回答いたします。 # この手の問題は、アルゴリズムではなく設計が問題だったりする為。 C4-------$79------A1------20----3030----2099 C4-------$80------A1-------1------12----1117 C4-------$62------A1----3030----3031----1802 C4-------$82------A1----1139----3023----1109 C4-------$83------A1----1803----1802----3031 C4-------$32------B1----1582-------1----3030 C4-------$85------B1----1109----1114----1117 上記データでの処理を書き出すと、以下の流れであっていますか? 1. 1行, 4要素[20]で検索する。 2. 1行, 5要素[3030]で検索する。 3. 3行, 4要素でヒット。3行目を別処理へ。 4. 6行, 6要素でヒット。6行目を別処理へ。 5. 1行, 6要素[2099]で検索する。 6. 2行, 4要素[1]で検索する 7. 6行, 5要素でヒット。6行目を別処理へ。 8. 2行, 5要素[12]で検索する (中略) ・3行, 4要素の[3030]や、6行, 5要素[1]では、検索しない。 (一度検索しているから) ・別処理を行った、3行目も、検索元になる。(3行, 5要素の[3031]では検索する) 上記の処理であっており、前処理が可能であるという前提でお話しします。 # 3万行x3要素を、3万行x3要素全てに対してチェックしている = 81億回のチェックを行っている まず、検索元数字は行数に関係なく処理を行っているので、まとめられます。また、二度同じ数字で検索を書けないのでそこも削れます。 <最も簡単な手法> 例えば4, 5, 6要素に現れる数字が1~9999なのであれば9999個の変数を用意することで無駄を省けます。 int element[10000]; elementに、全て0を代入する。 for (ファイルの終わりまで) { 要素を1つ読み出す; element[読み出した要素] = 1; } for (i = 1; i < 10000; i++) { for (ファイルの終わりまで) { 要素を1つ読み出す; if (element[i] == 読み出した要素) {その行を別処理へ;} } } ↑のソースは、要素を全部で9億回読み取っています。81億回→9億回の読み取り回数になりました。 # 処理内容が変わったので、処理時間が20%になったりはしませんが。 <多少面倒な方法> ハッシュを使います。 struct node { int number; int line_number; struct node *same_number; struct node *next_number; }; create_hash(struct node *p, read_number, read_line) { if (p == NULL) { p = 新しくメモリを確保する; p->number = read_number; p->line_number = read_line; p->same_number = p->next_number = NULL; } else if (p->number == read_number) { create_hash(p->same_number, read_number, read_line); } else { create_hash(p->next_number, read_number, read_line); } } } main() { struct node *element; element = NULL for (ファイルの終わりまで) { 要素を1つ読み出す; element = create_hash(element, 読み出した要素, 読み出した行); } for (ファイルの終わりまで) { 要素を1つ読み出す; ハッシュのp->numberが一致したら、p->same_numberがNULLになるまでp->line_numberを読み取り続ける 読み取ったline_numberは、別処理へ } } ↑のソースでは、ハッシュテーブルを作るのに9万回、行列を調べるのに9万回、併せて18万回要素を読み取っています。 81億回→18万回の読み取り回数です。 # 実はハッシュテーブルの検索に時間がかかるので、処理時間が0.002%になったりはしませんが。 以上、ご参考になれば幸いです。

sac411
質問者

補足

ご返事ありがとうございます。 p = 新しくメモリを確保する; ↑この場合どのようにメモリを確保したらよいでしょうか。 (mallocを使用するのでしょうか?) for (ファイルの終わりまで) { 要素を1つ読み出す; element = create_hash(element, 読み出した要素, 読み出した行); } ↑ここをもう少し詳しくお願いできませんでしょうか。 (日本語部分) だいたいのことは理解いたしました。 もう少し教えて頂ければ幸いです。 宜しくお願いします。

  • fonera
  • ベストアンサー率52% (38/72)
回答No.2

恐れ入ります。 データの検索方法について確認させてください。 >また、検索文字列は行ごとというわけではなく例えば >検索文字列 "67891" >aaa 123456 67892 23456 bbb ccc >aaa 123456 67891 23456 bbb ccc >aaa 123456 67832 23456 bbb ccc >(二行目だけを抽出したい) この場合、例えば「123456」を抜き出す可能性はありますか? つまり、検索対象は特定の列のみですか?(例では、3列目) 特定の列のみであれば、ソートしてから検索することで高速化が可能です。また削除してしまっても問題ありません。 特定の列ではなく、例えば >aaa 123456 67892 23456 bbb ccc >aab 123457 67891 23457 bbc ccd >aac 123458 67832 23458 bbd cce の様なデータがあったときに、 ・1回目は、[67892]を検索する ・2回目は、[bbc]を検索する このような処理を行いたい場合は、技術的には一工夫必要です。 ダミーのデータでも良いので、処理対象のデータと実際の検索手順を提示していただけませんでしょうか? (例えば、処理順序が重要な場合は検索元データはソートできませんし、抜き出した後の別処理に必要なデータが限定されていれば削れますし、検索ヒット率が低ければメモリに載せる必要もなかったりします)

sac411
質問者

補足

ご返信ありがとうございます。 具体的なというか使用する方法をそのままご説明させて頂きます。 C4-------$79------A1------20----3030----2099 C4-------$80------A1-------1------12----1117 C4-------$62------A1----3030----3031----1802 C4-------$82------A1----1139----3023----1109 C4-------$83------A1----1803----1802----3031 C4-------$32------B1----1582-------1----3030 C4-------$85------B1----1109----1114----1117 というような文字列が30000行あります。(データはそのまま使ってます) (-はブランクを意味します) 最初に一行目を取り出し、4番目の要素(------20)を同じファイル内で検索無ければ5番目の要素(----3030)を検索 三行目と四行目がヒットしたらその行を抜き出し、別の処理を行う 六番目の要素を検索し同様の手順を行う これを二行目の4、5、6要素の検索、三行目の4、5、6要素の検索とループさせています。 ブランクを含めた数字のみの検索を行うので、4、5、6要素以外にはヒットしないようになっています。 これでうまく説明になったでしょうか・・・ ご教示宜しくお願いします。

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.1

★アドバイス >現在は文字列を構造体→メモリを確保でなんとかやっておりますが、 >データの容量が大きい為、時間が掛かりすぎてしまいます。  ↑  どの程度でしょうか?  ファイルをメモリと同じように扱える方法があります。  『メモリ・マップド・ファイル』です。  http://marupeke296.com/DXCLS_MemoryMappedFile.html→『メモリマップドファイルクラス』  これが利用できる場合はメモリを確保してセットしない分高速になると思います。 >また、抜き出した行は検索する側、される側共に次以降二重処理して速度が >遅くなってしまうかと思うので、できれば削除したい。 >(現在は二重処理も致仕方ないとしてそのままやっています)  ↑  1行あたり1ビットのフラグで処理したら『1』とチェックします。  行数が30,000ですから3,750バイトで済みます。  抽出処理するときにはこのフラグを見て『0』の行だけを処理します。  これで重複処理を避けられます。 >検索する側から一行ずつ文字列を取得 >→特定のカラムから文字列を抜き出す >→検索される側からヒットした行を抜き出す >→別処理を行う  ↑  良く理解できていないと思いますが、膨大な文字列データから複数の文字列を一度に  検索して取り出したいのですか? ・過去に複数文字列の一括置換プログラムを作成したときの考えを書きます。  (1)置換する(検索)文字列をソートしてリスト化します。  (2)検索する(対象)文字列から行単位で検索を開始。   (a)行の1桁目から順番にリスト化したデータをバイナリーサーチ&リニアサーチ。   (b)リスト化したデータの長さより本当に一致したかを調査。   (c)一致したら抽出。   (d)次の桁に移動して(a)に戻る。行の末尾で終了。  (3)抽出処理した行はフラグを『1』にチェック。  (4)次の行へ移動して(2)に戻る。膨大な文字列データの最後に来たら全処理終了。  ※上記は置換ですが抽出(検索)も同じ考えていけると思います。  ※置換をしないで抽出すれば良いだけなので。  上記の方法でそこそこ高速化できます。  でも、もっと改良する余地がありそうです。  ※私の場合、これ以上の工夫はしませんでした。(笑) ・上記の置換する(検索)文字列をソートしてリスト化するときに、(検索)文字列の長さも管理します。  それから(a)のバイナリーサーチ&リニアサーチは  (1)最初はバイナリーサーチします。このときにリスト化のデータ長で比較(strncmpなどで)  (2)続いてリニアサーチします。   これは『abc』『abcd』『abcdef』が(検索)文字列だった場合に一番長い『abcdef』に   一致させるために必要です。 ・説明不十分と思いますので補足などをどうぞ。

sac411
質問者

補足

ご返信ありがとうございます。 イメージ的には理解することができました。 何点か補足させて頂きます。 >現在は文字列を構造体→メモリを確保でなんとかやっておりますが、 >データの容量が大きい為、時間が掛かりすぎてしまいます。 > ↑ > どの程度でしょうか? struct list { struct list *next; char *str; }; struct list *next; struct list *charstr; struct list *top = NULL; char Buff[75] while (fgets(Buff, 71, "ファイル") != NULL) { charstr = malloc(sizeof(struct list)); charstr->str = (char*)malloc(strlen(Buff) + 1); strcpy(charstr->str, Buff); if (top == NULL) { charstr->next = NULL; } else { charstr->next = top; } top = charstr; } こんな感じで30000行分メモリに格納しています。 フラグについてはまだ勉強不足でわかりません。 できれば説明等あるページを教えて頂ければと思います。 >良く理解できていないと思いますが、膨大な文字列データから複数の文字列を一度に検索して取り出したいのですか? 一つの検索文字列からファイル内にある検索文字列を含む行(検索文字列を含む行が複数ある可能性が有る為)全てを抽出したいと考えています。 また、検索文字列は行ごとというわけではなく例えば 検索文字列 "67891" aaa 123456 67892 23456 bbb ccc aaa 123456 67891 23456 bbb ccc aaa 123456 67832 23456 bbb ccc (二行目だけを抽出したい) 説明不足で申し訳ありませんが、宜しくお願い致します。

関連するQ&A

  • 文字列の検索アルゴリズム

    「私は世界一頭がいいので、数学なんか簡単すぎていいかげん飽きてきた。」 この文字列から「いいかげん」を検索するアルゴリズムにはどんなものがあるのでしょうか? 条件 ・100KB(5万字)程度のデータから10文字程度指定文字列を検索する。 ・100KBと元データもそれほど大きくないので、プログラムが簡単なアルゴリズムが良い。 ・そのアルゴリズムの解説がインターネット上にあること。 単純に「い」を探して合致したら次の文字も「い」であることを確認。 さらに次の文字が「か」であることを確認。 さらに・・・「げ」・・「ん」 とやっていく方法でも良いとは思うのですが、もしかしたらもっとお手軽かつ高速にできる方法があるのかなと思い質問してみました。 なければないという回答でもかまいません。 また、参考までにテキストエディタ等がどのようなアルゴリズムを用いているのか知っている方(推測できる方)はその方法についても教えて頂ければと思います。 よろしくお願いします。

  • 文字列の比較

    現在Cでプログラムをつくっているのですが いきずまってしまいました。 1.テキストファイルを読み込む 2.書き込みファイルを開く 3.読み込んだデータを一行読み込んで   その行の特定の文字列があれば、   特定の文字列のみ取り出し、   書き込みファイルに書く。    4.次以降の行も同じ処理をする。    5.読み込み、書き込みファイルを閉じる。 と、こんな感じのプログラムなのですが、 3の特定の文字列をどのように取り出せばいいのかわかりません。 取り出したいのが数字ならば、if文でできるのですが 文字列の場合は、どうなんでしょうか。 例えば、「MOJIRETU11」という取り出したいとき 数字と同じようにIF文を使用することは、できるのでしょうか。

  • ファイルの文字列の処理の質問

    今ファイルに対して文字列の処理をしています。 あるファイルに対して一定の文字列を検索して、その検索したい文字列が なければ、その一行をファイルに出力したいですが、手元にwindowsバージョン のgrep.exe で実現すると考えています。 しかし、検索したいファイルは、文字列が入ってない行があります。 そのため、検索したい文字列が存在しない行は、改行だけの行を結果として 出力されています。改行だけの行を除きたいですが、どうすればいいか? ファイルのsjisです。例えば、内容としては、以下のようになっています。 aiiiiii ballllll fafafa 777777 とするファイルがあります。そのファイルに対して、aという文字が入ってない行を 取りたいですが、実際にgrep -v "a" ファイル名 でやると、777777の行とすべて 改行だけある行が取られてました。 777777だけをとる方法がありますでしょうか?

  • 複数の文字列を含むファイルの検索-linux

    linuxにおいてファイル内の文字列を検索するのにgrepを用いますが、 複数の文字列を含むファイルを検索するにはどうしたらよいでしょうか。 同じ行ではなく別の行にある場合で、ファイル名を出力したいです。 つまり ~~~~~~~~~ ~~~~~~~~文字列1  ~~~~~~~~~~ ~~~~~~~~ ~~~~~~~文字列2 のような記述のあるファイルを探すにはどうしたらよいでしょうか。

  • 文字列検索

    文字列検索 テキストファイルの検索を行いたいです. 下記のようなサンプルファイルでfooを検索し, 含まれるならマッチした個数,含まれないならnilを返す関数を作りたいです. mecabを用いて形態素解析を1行ずつしようかと思ったんですが, 大量のファイルを処理する予定なので,オーバヘッドが気になります. 関数でgrepがあるみたいですが,マッチした行しか返されません. 標準関数で1行ずつよみこんで,1行のなかでマッチした回数を返す関数はありますか? --sample.txt-- foo foo bar bar foo hoge,hoge,hoge,hoge hage-hage-hage-foo -- end --

    • ベストアンサー
    • Ruby
  • 【C言語】大文字小文字に関わらず文字列を検索したい

    C言語で文字列を検索処理があるものですが、 以下のようなことがうまくできず困っています。 1.検索対象はファイルから読み込んだメモリ内の文字列。 2.対象の文字列は大文字小文字に関わらずに抽出する。 例えば、「abc」を検索するとして、文字列内が aaaaBccccdefgだとすると… aaa「aBc」cccdefg かっこ内を検索します。 strstrだと、完全な一致しかヒットしないので… 長い文字列が対象になることもあるので、 すべての組み合わせを見るのも性能的に難しいです。 何か方法ご存知の方いらっしゃいましたら、よろしくお願いします。

  • EXCEL・ 2つの列内にある文字データで一致するものを検索する方法

    始めまして 例えば 下記の様にA列内の文字データ(数字ですが実際には文字列) とB列内の文字データが同じものを検索する方法です。 C列に同じデータがあれば○と表示する方法でもいいです。 この場合123は検索対象となるので1行目のC列又は2行目のC列に 印か文字を表示させたいです。    A列 B列 1行 123 111 2行 456 123 3行 789 222 よろしくお願いします。

  • (文字列検索の手法について)検索エンジンはなぜあんなに高速で検索できるのですか?

    googleなどの検索エンジンについて。 どうやってあれほどのスピードで検索できるのか?と言う疑問です。 天文学的な数が存在するウェブサイト群から、ほんの一瞬で何万件とリストアップできるのはなぜでしょう? テキストデータとは言え、情報量にしたら何十?何百テラバイト?見当もつきませんが、 サーバー側にキャッシュが溜まっているとは言え、これらの内容全部を一瞬で走査することができるのが驚きです。 しかも利用者は私一人ではない、全世界中から次々と検索リクエストが来るのにも関わらずです。 私はかけだしのプログラマーですが、文字列検索のテストプログラムなどを作ってみても、 どうやってもあれほどのケタ違いのスピードは出せません。 自分のパソコンから検索するだけでもそれなりの時間がかかってしまいます。 とても不思議です。 後学のために、どのようなハードウェアで、どのような言語のプログラムで、 どのようなアルゴリズムで、あそこまでのスピードを実現しているのか教えていただきたいです。 よろしくお願いします。

  • 文字列検索とピックアップ

    EXCELの表があります。 今、この表のA1~A100までにある特定の文字列”αβγ“を含んだセル(含んだとしても1回のみ)と含まないセルがあります。含んだセルのある行のみを残して、他の行を削除したい。このための簡単な操作(処理)は何か? 一方法として、文字列検索をかけ、ピックアップされたものについてある列を使い1をたてるなどしてからソートしピックアップする以外の方法でかんがえつくものを教えてください。

  • 最も多い文字列を検索するには

    皆様いつもお世話になっております。 最も多い文字列を検索するにはどのようにすればよいでしょうか。 具体的には (1)A列に6文字の文字列が並んでいます。 (2)先頭4文字の文字列で最も多い種類の文字列の値を取得する (3)最も多い文字列以外の文字列を含む行を削除する というプログラムを組みたいと思います。 よろしくお願いします。