● VBでstrstrを使用して文字列を検索する ●

拙作「DLLにエクスポートされている関数を列挙するかもしれないツール」はその名の通り、DLLファイルの関数名とその序数を列挙してくれるツールである。ちょいとばかし懐かしさを感じつつ、遊び感覚で適当にDLLファイルをのぞいていたら、偶然それは起こった。読んだDLLファイルは shell32.dll で、表示された関数に目を見張ってしまった。要するに本当に驚いたのだ!! そんなわけで、驚きの部分を拾ってみた。なお Windows XP の shell32.dll 関数一覧はこちらを参照。

 387   StrRChrW
 388   StrRStrA
 389   StrRStrIA
 390   StrRStrIW
 391   StrRStrW
 392   StrStrA
 393   StrStrIA
 394   StrStrIW
 395   StrStrW
 396   WOWShellExecute
 164   Win32DeleteFile
 652   WriteCabinetState

分かりますか? なんとあこがれの strstr があるじゃあ〜りませんか!! さらに後ろから文字列を検索をしてくれる strrstr もある!! これはぁ〜本当にどえりゃぁ発見だよ。ところで私のパソコンの OS は WindowsXP(SP3) なのであるが、これらの関数はいつ追加されたのであろう。Windows XP 以前にはいなかったのであろうか? 非常に興味深いところである。ちなみに Windows95 の shell32.dll には strstr の姿は無い。こちらが Windows95 の shell32.dll にエクスポートされている関数一覧なのであるが、まあ適当に確認してくださいな。Windows進化の過程で関数の数がかなり増えていることが分かるね。なにはともあれ Microsoft グッジョブなのである!!

C言語的文字列関数 strlen、strcpy、strncpy、strcat に相当するの関数は、kernel32.dll にエクスポートされており、VBからでもAPI関数として普通に使用できる。その一方 strstr は残念ながら用意すらされていないようで、VBでは遠い遠い存在となっていた。strstr のDLLファイルを自作しようかと本当に思っていたくらいである、結局、作らなかったんだけどね。

不意に…、shell32.dll にある StrStrA や StrRStrA の存在は多くの人が知っており、ひょっとしたら私が知らなかっただけかと思い、そして心配になったので、Google先生にお伺いを立ててみた。その結果は2010年8月28日時点で、API宣言を挙げてさらにVB用サンプルを用意したサイトは見当たらなかった(strstrのAPI宣言のみが書いてあるサイトは1つあった)。ということは、ここが世界初なのかもしれないんだぜ!!

そんなわけで早速、さぐりを入れつつ strstr と strrstr のサンプルを書いてみた。strchr系はとりあえず放置。ついでに、メモリマップドファイルを嗜んでみるのサンプルプログラムに strstr を利用した文字列置換コードがあるのでそちらも参照されたい。

Private Declare Function strlen Lib "kernel32" Alias "lstrlenA" (ByVal lpString As Long) As Long
Private Declare Function strcpy Lib "kernel32" Alias "lstrcpyA" (ByVal lpString1 As Long, ByVal lpString2 As Long) As Long
Private Declare Function strstr Lib "shell32" Alias "StrStrA" (ByVal lpString1 As Long, ByVal lpString2 As Long) As Long
Private Declare Function strrstr Lib "shell32" Alias "StrRStrA" (ByVal lpStart As Long, ByVal lpEnd As Long, ByVal lpSearch As Long) As Long

'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
' 機  能:ファイルに特定文字列がいくつ含まれているか取得する
' 引  数:(i)TargetText … 対象文字列
'         (i)SearchText … 検索文字列
' 返り値:取得回数
' 備  考:strstrを使用するサンプル
'=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Public Function GetHitCount(ByVal TargetText As String, ByVal SearchText As String) As Long

'For文を使用する場合はTrueを設定、Do文を使用する場合はFalseを設定
#Const I_LIKE_FOR = False
    Dim i As Long
    Dim TargetTextA, SearchTextA As String
    Dim lpFound As Long   'strstrが返す、検索文字列発見位置のポインタ
    Dim lpTarget&, lpSrch As Long
    Dim nSize As Long
    Dim lpStart&, lpEnd As Long

    'Unicode→ANSI変換じゃ
    TargetTextA = StrConv(TargetText, vbFromUnicode)
    SearchTextA = StrConv(SearchText, vbFromUnicode)

    '対象文字列のポインタ、検索文字列のポインタとサイズを取得する
    lpTarget = StrPtr(TargetTextA)
    lpSrch = StrPtr(SearchTextA)
    nSize = LenB(SearchTextA)

    '検索開始位置と終了位置を取得する
    lpStart = lpTarget
    lpEnd = lpTarget + strlen(lpTarget)

'For文を使用する場合
#If I_LIKE_FOR Then
    'ポインタの先頭から後尾まで検索
    For i = lpStart To lpEnd
        '文字列検索
        lpFound = strstr(i, lpSrch)
        '見つからない場合は終了
        If lpFound = 0 Then Exit For
        '見つかった回数をカウントする
        GetHitCount = GetHitCount + 1
        '見つかった場合は次回検索位置を設定する
        'iはFor文でインクリメントされるので1を引く必要がある
        i = lpFound + (nSize - 1)
    Next i
'Do文を使用する場合
#Else
    'ポインタの先頭から後尾まで検索
    Do
        '文字列検索
        lpFound = strstr(lpStart, lpSrch)
        '見つからない場合は終了
        If lpFound = 0 Then Exit Do
        '見つかった回数をカウントする
        GetHitCount = GetHitCount + 1
        '見つかった場合は次回検索位置を設定する
        lpStart = lpFound + nSize
    Loop Until (lpStart > lpEnd)
#End If

End Function

'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
' 機  能:フルパスからファイル名 または 拡張子 を取得する
' 引  数:(i)FileName  … ファイル名(フルパス)
'         (i)IsModeExt … False:ファイル名を取得  True:拡張子を取得
' 返り値:ファイル名 または 拡張子  取得できない場合は空文字
' 備  考:strrstrを使用するサンプル
'=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Public Function GetFileNameEx(ByVal FileName As String, _
                              Optional ByVal IsModeExt As Boolean = False)
    Dim lpStart&, lpEnd&, lpFound As Long
    Dim SearchChar As String

    '拡張子を取得する場合は、まずファイル名を取得する
    If IsModeExt Then
        GetFileNameEx = GetFileNameEx(FileName)
        If FileName = "" Then Exit Function
    End If

    'Unicode→ANSI変換
    FileName = StrConv(FileName, vbFromUnicode)
    SearchChar = StrConv(IIf(IsModeExt, ".", "\"), vbFromUnicode)

    '検索開始位置と検索終了位置
    lpStart = StrPtr(FileName)
    lpEnd = lpStart + LenB(FileName)

    '後ろから検索
    lpFound = strrstr(lpStart, lpEnd, StrPtr(SearchChar))

    '"\" または "." を発見、しかもそれが最後尾の文字ではない
    If lpFound > 0 And lpFound <> lpEnd - 1 Then
        Dim Buffer() As Byte
        ReDim Buffer((lpEnd - lpStart - 1) - 1) As Byte
        Call strcpy(VarPtr(Buffer(0)), lpFound + 1)
        GetFileNameEx = StrNullCut(StrConv(Buffer, vbUnicode))
    '見つからない場合は空文字設定(拡張子取得時は再帰するのでこのElse文の処理が必要)
    Else
        GetFileNameEx = ""
    End If
End Function

'-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
' 機  能:文字列を Chr$(0)[=vbNullChar] まで取得する
' 引  数:(in)SrcStr … 対象文字列
' 返り値:編集された文字列
'=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Private Function StrNullCut(ByVal SrcStr As String) As String
    Dim NullCharPos As Long
    NullCharPos = InStr(SrcStr, Chr$(0))
    If NullCharPos = 0 Then
        StrNullCut = SrcStr
        Exit Function
    End If
    StrNullCut = Left$(SrcStr, NullCharPos - 1)
End Function

戻る