現在GREP機能を追加しているのですが秀丸等に比べて
検索時間が掛かりすぎている事に気づきました。
検索方法として、ファイルを開きながら一行毎
InStrで対象文字を含むか判定しています。
環境はVB6です。どなたかご存知の方は教えて下さい
秀丸は使ったことないので、推測ですけど、
恐らくスレッドを複数に分けたりして、やっているのではないでしょうか?
メインスレッドと検索スレッドを分けるだけでも、体感的に早くなる部分があるでしょうし、
それに加えて、正規表現を用いたり、ファイルの入出力アルゴリズムを工夫したり、いろいろなことをやっているのだと思います。
上のスレッド分割は、VBにはできないことです。プロセスごと分ける(別EXEにやらせる)という手もありますが…(この場合、使いづらいかな)。
正規表現はBRegExpなどのDLLや、CreateObjectを使って、VBScriptの正規表現機構を呼び出して使うこともできます。
ファイル入出力アルゴリズムは、よく知りません。バイナリファイルとして一括で読み込んだほうが、ひょっとしたら早いかもしれません。
(Binaryで開き、文字列変数に押し込む)
でもどちらにせよ、VBだけではやや難しいところがありそうですね。
C言語で検索用のプログラムを作るか、真ん中(速度と難しさの両方がVBとC言語の中間くらいと言う意味)を取ってDelphiを使うか…。
>ファイルを開きながら一行毎InStrで対象文字を含むか判定しています。
バイオスのバッファリングなんかで、ファイルはかなり先まで読まれています
から1行ごとにやらずに20行とか40行で、ことによったら全て読み込んでおいてInStrをやった方が早くなりませんか。
たかみちえさん、ねろさん回答ありがとうございます。
先ほどロジックを見直していたらファイルを一行読む毎に、
検索結果を表示するフォームをリフレッシュしていたのが
一番の遅さの原因と分かりました。
(なぜあんな事をしていたのだろう・・)
それだけでも条件によって8分ぐらい掛かっていたのが
30秒程に短縮できました。ねろさんのやり方でもやって
みます。
ねろさんの方法で質問なのですが、
バッファリングでかなり先まで読まれている
との事ですが、20行毎読み込むとした場合
Loopで20行分まわして変数に入れると言う事
ですか?それとも違う方法があるのでしょうか?
どうか教えて下さい。
そうです、出来れば一回で全部読み込みます。
仮に読み込む文字が20M位以下で、InStrでmeetする頻度があまり多くないと
すれば、文字列の配列を宣言しておいて、一つの文字列が500から1500位になるように読み込んだ文字列を足し込み、用意した配列に入れて、一挙に最後まで読み込みます。
ハードディスクのアクセスランプが着きっ放しになれば最高ですが。
すべてファイルを読み込んだら今度は再度、文字列の配列を足し込んで一つの文字列にします。そして最後にInStrで検索をかけます。
やって見ないと判りませんが、最終の文字列は1M位にしてInStrをかけた方が良いかも知れません。
20M以上の場合は、コンピューターのメモリーの大きさにもよりますが、20M位読み込んだところで一回上の処理をします。
同じファイルで文字列の長さを変え一番処理時間が少なくなる長さを決めます。
ねろさんありがとうございます。
説明不足の点がありましたので追加します。
検索結果としてファイル名と行番号を出力しています。
こな場合は、やはり一行毎のInStrしか方法はないのでしょうか?
ねろさんの方法を使いたいですが、行番号を取得するには
処理が複雑になる気がするので・・。
どうか教えて下さい。
行を探す方法は沢山あると思います。
1、1行の長さをスペースなどでパディングして同じ長さにあわせる。
2、配列に行の合計を書き込んでいく方法。要するにテーブル参照。
3、特殊文字を埋め込む
1の方法は行の最大の長さが60文字とすると、55文字の時はスペースを5つ追加する。
こうしておくとたとえばInStrで1632などと出たときは
IIF(1632\60=1632/60,1632\60,1632\60+1)などで行数が出る。
2の方法は文字文字通り一つの配列にはそれぞれの行のそれまでの積算の文字数を入れ、
d(1)=55;d(2)=90;d(3)=140;d(4)=160..などとなる、これだけだとInStrで出た数字を頭から比べなくては
ならないが、一工夫してd()の配列を一回なめ、50の倍数以上が最初に現れた行をe()に入れて行く
e(1)=1;e(2)=3;e(3)=4...などとなる、そこでd(e(1632\50))から比べ始める。
ほかに2分検索でもいいし
3はたとえば行の後ろに$%$PAGE0030などと特殊文字を埋め込むInStrで1632と出たらもう一度1632からInStrをかけて$SPAGEが出てきた次から8文字取ればそのままページ表示が出来るし、これがいいかな。
もし$1$が出現する可能性があるなら、CHR(13) & CHR(13) & "PAGE0030" などとする手も。
要するにいくらでも....
ねろさんいつもありがとうございます。
大変勉強になりました。早速やってみます。
↑上に書いたんですが、気になったのでa-Zまでのランダムの60文字の文字列を
10万行で、約6MのテキストをInStr有り、無しで比較したところ、
無しで0.484秒、有りで0.574秒でした。ほとんど変わりません。
どうも違う所に突っ込んでいたようです。(^^;
私の環境はPen3、、 1.7GHzですからそんなに速い環境ではありませんが、
エディタさんが30秒かかるということは、よほどファイル数が多いか
大きなファイルを開いているのですか?
差し支えなかったら教えて下さい。
30秒掛かったのには訳があります。現在検索する方法として
リストボックスに検索対象を複数取り込み先頭の文字で検索し
見つかれば次のファイルへ、ファイルが無くなれば次の検索対象
で検索するようにしています。(最初に言っておくべきでした)
10文字で10ファイルあれば100回Openしています。全サイズでも
2Mもないはずです。しかし検索対象が一つしかなくてもサイズ、
ファイル数が大きくなると速度は秀丸等に比べて遅くなります。
(ウィルススキャンが走るとなお遅い)たぶん6Mだったらもっと
差がはっきりするような気がします。ねろさんには本当にお手数を
おかけします。すいません、説明不足の点がありまして。
状況は良くわかりました。
私の上の6Mのファイルですがこの中から
"a"をInStrで探しListboxに位置を書き込む処理を
Open Close を含めて100回やってみました。
もちろん一行毎処理しました。
結果60740個の"a"の位置が書き込まれましたが、
その処理時間は2.235秒でした。
ちなみにListBoxに積み上げないで、配列に位置を書き込むと
0.547秒でした。
結論として
100回程度のOpen Closeでは遅くならない。
InStrは十分速い。
ということで、エディタさんの100Loopというのは、速くなる可能性はありますが
とりあえずボトルネックではなさそうです。
もしかしたらエディタさんのLoopの処理の中にその都度回数か何かを表示して
DoEventsなどとしていないでしょうか、これをやりますと
その度にWindowsに処理が戻り、思わぬ時間がかかります。
ねろさんのご指摘通りDoEventsが入ってました。
しかも一行読む毎に(^^。DoEventsは1000行毎に行う
事にしたのですが、やはり遅いです。以下に簡単な
テスト結果を書きます。
対象ファイル数:3
総サイズ:10M
検索対象位置:行末
検索方法:一行毎にInStrを行う(ファイルは全て読む)
DoEvents:一行読む毎に行う
1.DoEvents、検索を行う 25s
2.DoEventsを無くし検索のみ 15s
3.DoEvents、検索を行わない 7s
4.DoEventsのみ 14s
5.DoEventsは1000行,10000行毎行い、検索もする ともに16s
仮に秀丸なら3,4sぐらいです。検索もせずただ読み込むだけでも
7s程掛かるので検索ロジック以前の問題の様です。うーん、もう
手の打ちようがないのでしょうか?試しにBinaryで読んで見たら
とてつもなく遅くなりました。我慢するしかないのですかね?
ためしに6Mでは検索のみで7sでした。環境はAMD Athlon800MHzですから
ねろさんとの差はそこなのでしょうか?
ウーン、日進月歩ですね
DoEventsに関しては最初に聞くべきでした。(^^;
秀丸の場合たぶんスピードが要るところはCではなくアッセンブル言語か
何かでやっているでしょうから、VBと比べるのは難しいでしょうが、
読み込みの7sは置いておいて、検索の8sを短くしたいですよね。
読み込んで検索だけをして(t=InStr(s,"***")だけやってあとは全てコメントアウト)
これで8秒近くかかるのであれば検索に関してはこれが限界ということになります。
Binary読み込みですが1Byteずつやっていたのではだめです。
VarString = String(2048," ") Get #1,,VarString などとバッファを大きくとって
一気に読み込みます、端数はファイルの長さを計算しバッファの長さを変えて読み込みます。
ただこの後行の切り出し処理がありますから、これで速くなるかどうかは判りません。
読み込み速度に関してはあきらめでしょうか。
ツイート | ![]() |