fd-ricky

パソコン関係のメモが多め

VBAで全て検索と同等の関数作った

作るきっかけ

  • FindからのFindNextの処理を毎回検索して書き直すのがめんどくさい
  • メインの処理にDo Loopが入って見にくい

使用上の注意点

  • Raiseするエラー番号がテキトー
  • RangeのCollectionだから検索対象のブックを閉じると参照できなくなる

全て検索する関数

とりあえず書いたけど、もっとスマートにできそう。

Public Function FindAll(ByRef What As String, ByRef Scope As Variant) As Collection
    ' 検索と置換の[全て検索(I)]に近い関数
    '
    ' Args:
    '       What (String): 検索対象文字列。 Cells.[Find|FindNext]に渡すWhat。
    '       Scope (Workbook|Worksheet): 検索範囲を示す。渡されたオブジェクトの全体を検索
    '
    ' Returns:
    '       Result: (Collection): 検索にヒットしたRangeのCollection
    '
    
    ' Scopeの型判定
    Dim TargetSheets As Object
    Select Case TypeName(Scope)
        Case "Workbook"
            Set TargetSheets = Scope.Worksheets
        Case "Worksheet"
            Set TargetSheets = New Collection
            TargetSheets.Add Scope
        Case Else
            Err.Raise 999, Scope, "Scopeの型はWorkbookかWorksheetにして下さい。"
    End Select
    ' Return用Collection
    Dim Result As Collection: Set Result = New Collection
    ' 検索結果の一時格納変数
    Dim FoundCell1 As Range, FoundCell2 As Range
    ' ループ用Worksheet
    Dim Ws As Worksheet
    For Each Ws In TargetSheets
        ' 最初の検索
        Set FoundCell1 = Ws.Cells.Find(What:=What, LookIn:=xlValues, LookAt:=xlPart)
        ' 検索結果が無ければ次のシートに
        If FoundCell1 Is Nothing Then GoTo NextWs
        ' 検索結果をCollectionに格納
        Result.Add FoundCell1
        ' シート内を複数検索するためのループ
        Do
            ' 次のセルを検索
            Set FoundCell2 = Ws.Cells.FindNext(After:=Result(Result.Count))
            ' ヒットしたセル番地と最初に検索したセルの番地が同じだったら次のシートに
            If FoundCell1.Address(external:=True) = FoundCell2.Address(external:=True) Then Exit Do
            ' 検索結果をCollectionに格納
            Result.Add FoundCell2
        Loop
NextWs: ' 他言語のContinue代わり
    Next
    Set FindAll = Result
End Function

使い方

Scopeに検索対象のオブジェクトを渡す。

Sub Test()
    Dim FoundCells As Collection, FoundCell As Range
    
    ' シート内全検索
    Set FoundCells = FindAll("A", Sheet1)
    For Each FoundCell In FoundCells
        Debug.Print FoundCell.Address(external:=True); vbTab; FoundCell.Value
    Next
    
    ' ブック内全検索
    Set FoundCells = FindAll("A", ThisWorkbook)
    For Each FoundCell In FoundCells
        Debug.Print FoundCell.Address(external:=True); vbTab; FoundCell.Value
    Next
End Sub

イミディエイトウィンドウの出力

[FindAll.xlsm]Sheet1!$B$2    A
[FindAll.xlsm]Sheet1!$C$5    ASC
[FindAll.xlsm]Sheet1!$B$6    AB
[FindAll.xlsm]Sheet1!$B$7    aaA
[FindAll.xlsm]Sheet1!$B$2    A
[FindAll.xlsm]Sheet1!$C$5    ASC
[FindAll.xlsm]Sheet1!$B$6    AB
[FindAll.xlsm]Sheet1!$B$7    aaA
[FindAll.xlsm]Sheet2!$B$2    AC
[FindAll.xlsm]Sheet2!$C$4    AD
[FindAll.xlsm]Sheet2!$B$6    AV