[リストへもどる]
一括表示

投稿時間:2003/11/13(Thu) 15:45
投稿者名:ちり
Eメール:
URL :
タイトル:
配列の中の文字列を検索しその位置を知るには
Private S() As String

Function AAA(ifst As Long) As Long
    Dim i As Long

    For i = ifst To UBound(S) - 1
        If InStr(S(i), "etc") > 0 Then
            AAA = i
            Exit For
        End If
    Next i

End Function

このような感じで、ある文字列(この場合"etc")を含む配列の番号を所得したいのですが、
S()が大きくなると、異様に速度が落ちてしまいます。
もっと早く検索する方法はあるのでしょうか?

winXP(SP1)
vb6(SP5)

投稿時間:2003/11/13(Thu) 22:06
投稿者名:よねKEN
Eメール:
URL :
タイトル:
Re: 配列の中の文字列を検索しその位置を知るには
> このような感じで、ある文字列(この場合"etc")を含む配列の番号を所得したいのですが、
> S()が大きくなると、異様に速度が落ちてしまいます。
> もっと早く検索する方法はあるのでしょうか?

提示のコードの部分だけで何とかしようとすると、
この前提でこれ以上速くするのはおそらく無理でしょう。

・このプログラムは全体として何をしようとしているのか?
・全体の処理フローはどんな構造になっているか?
・さらにこのコードの部分はどんな役割なのか?

等を考慮して、プログラム全体として最適化することが必要ではないでしょうか。

例えば、この関数を呼ぶまでの間に、
配列Sにどこかで値を設定している箇所があるはずですが、
その設定するときに"etc"が含まれている部分を収納した配列を別途用意するとか。
全体の処理フローと個々の処理の役割がわかれば、また、別のアプローチもあると思います。

投稿時間:2003/11/14(Fri) 10:33
投稿者名:ちり
Eメール:
URL :
タイトル:
Re^2: 配列の中の文字列を検索しその位置を知るには

目的は、平均60Mくらいのテキストデータの中から、ある文字列を探し出し、
その文字列を含む行の内容を変更する。という作業です。
また、変更するための検索文字列はそのテキストデータに記述されていて、
変更する検索文字列の数もかなり多いのです。

ですから、

Open filePath For Input As #tmp
Txt = StrConv(InputB(LOF(tmp), 1), vbUnicode)
Close #tmp

S = Split(Txt, vbCrLf)

という感じでテキストデータを読み込み、
まず配列 S に格納しています。

その後、配列 S の中身を検索して、検索文字列を別の配列 Sb に格納し、


Function AAA(ifst As Long, strETC As String) As Long
    Dim i As Long
    For i = ifst To UBound(S) - 1
        If InStr(S(i), strETC) > 0 Then
            AAA = i
            Exit For
        End If
    Next i
End Function

このような関数を使って

For i1 = 0 To UBound(SB) - 1
    For i2 = 0 To UBound(s) - 1
        n = AAA(i2, SB(i1))
        '中身を書き換えるため処理
        BBB n
        i2 = n
    Next i2
Next i1

という感じで処理していますが、処理がかなり遅いため、
とても実用的とはいえません。
i1,i2 のループは処理上しょうがないとして、
AAA の処理をイメージ的には、

    n = InStrArr(startNo, S(), strETC)

というような感じで、
InStr の配列版のような処理があればと思い投稿しました。

投稿時間:2003/11/17(Mon) 00:43
投稿者名:Say
Eメール:
URL :
タイトル:
Re^3: 配列の中の文字列を検索しその位置を知るには
状況にもよりますが、InStrよりLikeのほうが速いのではないかと・・・。

投稿時間:2003/11/18(Tue) 10:16
投稿者名:
Eメール:
URL :
タイトル:
Re: 配列の中の文字列を検索しその位置を知るには
1、1行毎に読んでるデータを20行など、配列上まとめて読み込み1度で検索。
2、エディタ等を開き、その検索を使用する。
3、データ検索DLLを作成
などがありますが、いちばん簡単なのは1番ですね^^
それだけでもだいぶ変わると思います。

投稿時間:2003/11/18(Tue) 12:45
投稿者名:ちり
Eメール:
URL :
タイトル:
Re^2: 配列の中の文字列を検索しその位置を知るには

みなさん回答ありがとうございます。
Like演算子って知りませんでした。試してみます。

20行まとめて読み込みってのは意外な処理ですね。
まったく思いつきませんでした。これも試してみます。

エディタ等で手作業するのはちょっと厳しいと思い、
vbで処理しようとしています。

DLLを作成ってのは、スキル不足なので不可能そうです。


いろいろアイディアをいただきましたので、また、いろいろ検討してみます。
ちなみに、現在の状況なんですけど、

データを読み込んで、配列に入れてfor〜nextで処理するに限界を感じて、
ADO2.5で処理しようとしたが、60Mのデータが70〜80Mのmdbになり、
その読み込みの遅さと処理の遅さで断念して、
最終的に、データを読み込んだString型の変数で処理しています。


    Private Sdat As String
    Private Sdat1 As Long, Sdat3 As Long
    Private Sdat2 As String

Function SdatBunkatu(iFst As Long, strKen As String) As Long
    Dim Si As Long

    '目的の文字が含まれていない
    Si = InStr(iFst, Sdat, strKen)
    If Si = 0 Then Exit Function
    
    Sdat1 = InStrRev(Sdat, vbCrLf, Si) + 1
    Sdat3 = InStr(Si, Sdat, vbCrLf)

    Sdat2 = Mid(Sdat, Sdat1 + 1, Sdat3 - Sdat1 - 1)
    Sdat2b = Sdat2

    SdatBunkatu = iFst + 1

End Function

というかんじで目的の行を Sdat2 に入れて変換処理し、

Function SdatKetugo()

'    Sdat = Mid(Sdat, 1, Sdat1) & Sdat2 & Mid(Sdat, Sdat3)
    Sdat = Replace(Sdat, Sdat2b, Sdat2)

End Function

でもとの変数を差し替える。という感じで処理しています。
いろいろ試した中でこれが一番早かったのですが、
実用できるようなスピードが出ていません。

また、なにか良い知恵があればご教授お願いします。

投稿時間:2003/11/18(Tue) 14:11
投稿者名:花ちゃん
Eメール:
URL :
タイトル:
Re^3: 配列の中の文字列を検索しその位置を知るには
検索・置き換えするだけなら、配列に読込まずに
Re^3: 条件に合った行の削除・・・ - KG さん 11/15-18:27 No.6608 の投稿の
ような方法をとればかなり早くなるかと思います。
現在どの位の処理速度か知りませんが、上記方法ですと、読込んで
検索・置き換えして保存までの処理が、1分以内でできると思います。

投稿時間:2003/11/18(Tue) 16:04
投稿者名:
Eメール:
URL :
タイトル:
Re^3: 配列の中の文字列を検索しその位置を知るには
20行ってちょっと間違ってましたね。
60Mのファイルをseekで20回位に分けて読み込み、
置換を行うという意味でした^^;
ちなみにσ^^のPCで60Mのファイル読み込み・置換を行うと、
1、ちりさんのように一度に読む場合 = 6分30秒位
2、3M単位のLoop = 21秒でした。

VB6.0
Windows2000使用

一部抜粋

    ' ポインター計算
    Seek #1, lngFilePointer                 ' 既に読み込まれている分は飛ばす
    
    strUni = StrConv(InputB(lngReadSize, #1), vbUnicode)    ' SJISからUNICODEへ
    
    Do
        ' STR文字を検索、定数の方が少し早いかも。
        ret = InStr(1, strUni, "STR")
        If ret = 0 Then Exit Do

        ' STR文字をETRに変換
        strUni = Mid$(strUni, 1, ret - 1) & "ETR" & _
        Mid$(strUni, ret + Len("STR"), Len(strUni) - ret + Len("STR"))
    Loop
        
    lngFilePointer = Seek(1)                ' 次の読み込み位置更新

Loop

投稿時間:2003/11/18(Tue) 18:14
投稿者名:ちり
Eメール:
URL :
タイトル:
Re^4: 配列の中の文字列を検索しその位置を知るには
回答ありがとうございます。

確かに KG さんの処理をした場合、
30秒程度しかかからずかなり高速に感じました。
しかし、このような工程を何万回かしないといけないので
途方にくれているのです。ですから、
その処理回数を減らす努力を今しています。

雅 さんの処理も同様にかなり早かったです。
でも、検索文字列が長い場合に、取りこぼしが出てきたので
その辺をうまく処理出来るようにしないと厳しそうです。

読み込み処理で高速になったので、
かなり希望がみえてきました。
ありがとうございました。

投稿時間:2003/11/19(Wed) 11:13
投稿者名:よねKEN
Eメール:
URL :
タイトル:
Re^5: 配列の中の文字列を検索しその位置を知るには
> 雅 さんの処理も同様にかなり早かったです。
> でも、検索文字列が長い場合に、取りこぼしが出てきたので
> その辺をうまく処理出来るようにしないと厳しそうです。

検索元データを分割して処理しようとする場合は、
分割の境界に気を付けないと取りこぼしが出ます。
分割の境界部分で(検索文字列の文字数-1)分だけ余分に重複させるとよいですよ。