2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Excel全セル検索ツールをVBAで

Posted at

はじめに

業務で「Excelのどこにこの単語があるのか分からない…」ということはありませんか?
複数のフォルダにまたがってファイルを探すのは、手作業だと非常に大変です。

今回紹介するVBAツールは、指定フォルダ(サブフォルダ含む)内のすべてのExcelブックを検索し、
ヒットしたセルの「ファイル名・シート名・セル番地・見出し列」を一覧化します。

特徴
• フォルダ単位の一括検索(サブフォルダ含む)
• 複数の検索ワードを一括指定可能
• 結果を1枚のUIシートに整形出力
• Nullや日付書式も安全に処理(Null/CVErr対策、日付は yyyy-mm-dd で統一)
• Excelのみで完結(外部DLL不要)

使い方
1. このコードを標準モジュールに貼り付けます。
2. run_BuildUI を実行すると、SearchUI という管理シートが自動生成されます。
3. C2 にフォルダパスを入力します。
4. C5 以降に検索したいキーワードを1行ずつ入力します。
5. 「Run Search」ボタンを押すだけ。

出力される情報

項目 内容
File (relative) サブフォルダ含むファイル名
Search Word 検索ワード
Sheet シート名
Cell セル番地
Header(Row1) その列の1行目の見出し

処理の流れ
1. 指定フォルダ以下のすべてのExcelファイルを再帰的に列挙
2. 各ブックを読み取り専用で開く
3. 全シート・全セルを UsedRange から配列に一括ロード
4. 各セルの値を to_検索用文字列 関数で安全に文字列化
5. 検索語ごとに InStr で照合
6. ヒットした情報をUIシートに追記

補足:to_検索用文字列 の役割

この小関数が本ツールの肝です。
• Null / Empty / #N/A などを安全に空文字扱い
• 日付セルは "yyyy-mm-dd" に整形
• その他は CStr で標準化

これにより、型エラーを起こさず、業務データを安定して検索できます。

想定シナリオ
• 過去の請求書や報告書から「特定の取引先名」を検索
• 大量のExcel定義書から「特定の列名」や「変数名」を抽出
• RPAやETLの影響範囲調査
• 仕様書群の横断的キーワード検索

注意点
• 大規模フォルダを対象にすると時間がかかります。
→ フィルタ条件を絞る・サブフォルダを分割するのがおすすめ。
• Excelが同時に他のマクロを実行中だと競合する場合があります。
• .xlsb なども対象に含まれます。不要なら collect_Excelファイル再帰 内の拡張子を調整してください。

おわりに

業務で「Excel全文検索が欲しい」と思ったときに、
自分で書ける・自分で改造できるのがVBAの強みです。

ぜひ社内利用・RPA補助・監査対応などに活用してみてください。
(コードは下に貼り付けてご利用ください)

Option Explicit

'========================
' Entry points (buttons)
'========================
Public Sub run_BuildUI()
    '=== UIシートを新規作成または再利用し見出しとボタン配置を行う ===
    Dim ws_UIシート As Worksheet
    On Error Resume Next
    Set ws_UIシート = ThisWorkbook.Worksheets("SearchUI")
    On Error GoTo 0
    
    If ws_UIシート Is Nothing Then
        Set ws_UIシート = ThisWorkbook.Worksheets.Add(After:=Sheets(Sheets.Count))
        ws_UIシート.Name = "SearchUI"
    Else
        ws_UIシート.Cells.Clear
    End If
    
    '=== UIシートのレイアウトを初期化する ===
    With ws_UIシート
        .Range("A1").Value = "Excel 全文検索ツール"
        .Range("B2").Value = "Base Folder:"
        .Range("C2").Value = ""
        .Range("B4").Value = "Search Words (one per row):"
        .Range("C4").Value = "← C5以降に1語/行で入力"
        
        .Range("A7").Value = "Results:"
        .Range("A8:E8").Value = Array("File (relative)", "Search Word", "Sheet", "Cell", "Header(Row1)")
        .Columns("A:E").ColumnWidth = 28
        .Rows("8:8").Font.Bold = True
    End With
    
    '=== UI操作用ボタンを配置する ===
    add_UIボタン ws_UIシート, "Pick Folder", "run_PickFolder", "E2", 120, 22
    add_UIボタン ws_UIシート, "Run Search", "run_SearchAll", "E4", 120, 28
    add_UIボタン ws_UIシート, "Clear Results", "run_ClearResults", "E5", 120, 22
    
    ws_UIシート.Activate
    MsgBox "SearchUI を準備しました。C2にフォルダ、C5以降に検索語を入力してください。", vbInformation
End Sub

Public Sub run_PickFolder()
    '=== フォルダ選択ダイアログを開いてパスを設定する ===
    Dim ws_UIシート As Worksheet: Set ws_UIシート = get_UIシート()
    Dim str_選択フォルダ As String: str_選択フォルダ = get_フォルダ選択パス()
    If Len(str_選択フォルダ) > 0 Then ws_UIシート.Range("C2").Value = str_選択フォルダ
End Sub

Public Sub run_ClearResults()
    '=== 既存の検索結果をクリアする ===
    Dim ws_UIシート As Worksheet: Set ws_UIシート = get_UIシート()
    Dim lng_最終行 As Long
    lng_最終行 = ws_UIシート.Cells(ws_UIシート.Rows.Count, "A").End(xlUp).Row
    If lng_最終行 > 8 Then ws_UIシート.Rows("9:" & lng_最終行).ClearContents
End Sub

Public Sub run_SearchAll()
    '=== 入力値を取得し前提条件を検証する ===
    Dim ws_UIシート As Worksheet: Set ws_UIシート = get_UIシート()
    
    Dim str_基準フォルダ As String
    str_基準フォルダ = Trim(CStr(ws_UIシート.Range("C2").Value))
    If Len(str_基準フォルダ) = 0 Then
        MsgBox "C2 に基準フォルダパスを入力してください。", vbExclamation
        Exit Sub
    End If
    If Right(str_基準フォルダ, 1) = "\" Then str_基準フォルダ = Left(str_基準フォルダ, Len(str_基準フォルダ) - 1)
    If Dir(str_基準フォルダ, vbDirectory) = "" Then
        MsgBox "フォルダが見つかりません: " & str_基準フォルダ, vbCritical
        Exit Sub
    End If
    
    '=== 検索語を取得してコレクション化する ===
    Dim col_検索語 As Collection
    Set col_検索語 = get_検索語コレクション(ws_UIシート)
    If col_検索語 Is Nothing Or col_検索語.Count = 0 Then
        MsgBox "検索ワードがありません。C5以降に1語/行で入力してください。", vbExclamation
        Exit Sub
    End If
    
    '=== 検索結果の表示領域を初期化する ===
    run_ClearResults
    
    '=== 対象フォルダ以下のExcelファイル一覧を再帰的に収集する ===
    Dim col_対象ファイル As Collection
    Set col_対象ファイル = New Collection
    collect_Excelファイル再帰 str_基準フォルダ, col_対象ファイル
    If col_対象ファイル.Count = 0 Then
        MsgBox "Excelファイルが見つかりません。", vbInformation
        Exit Sub
    End If
    
    '=== パフォーマンス向上のためアプリ設定を一時変更する ===
    Dim xl_計算モード As XlCalculation
    Dim bln_画面更新 As Boolean, bln_イベント As Boolean
    bln_画面更新 = Application.ScreenUpdating: Application.ScreenUpdating = False
    bln_イベント = Application.EnableEvents: Application.EnableEvents = False
    xl_計算モード = Application.Calculation: Application.Calculation = xlCalculationManual
    
    '=== 走査処理の初期値を設定する ===
    Dim lng_ファイルIndex As Long
    Dim wb_対象ブック As Workbook
    Dim lng_ヒット件数 As Long: lng_ヒット件数 = 0
    Dim lng_次行 As Long: lng_次行 = 9
    
    On Error GoTo CLEANUP
    
    '=== 各Excelファイルを順次開いて処理する ===
    For lng_ファイルIndex = 1 To col_対象ファイル.Count
        Dim str_フルパス As String: str_フルパス = CStr(col_対象ファイル(lng_ファイルIndex))
        Set wb_対象ブック = Workbooks.Open(Filename:=str_フルパス, ReadOnly:=True, UpdateLinks:=0, IgnoreReadOnlyRecommended:=True)
        
        '=== ブック内の全ワークシートを走査する ===
        Dim ws_対象シート As Worksheet
        For Each ws_対象シート In wb_対象ブック.Worksheets
            
            '=== シートの使用範囲を取得する(空シートはスキップ) ===
            Dim rng_使用範囲 As Range
            Set rng_使用範囲 = get_使用範囲_空対応(ws_対象シート)
            If Not rng_使用範囲 Is Nothing Then
                
                '=== 使用範囲を配列に読み込み高速に走査する ===
                Dim arr_ As Variant
                arr_ = rng_使用範囲.Value2
                
                '=== 配列の下限・上限を取得する ===
                Dim lng_ As Long, lng_ As Long
                Dim lng_L As Long, lng_U As Long, lng_L As Long, lng_U As Long
                lng_L = LBound(arr_, 1): lng_U = UBound(arr_, 1)
                lng_L = LBound(arr_, 2): lng_U = UBound(arr_, 2)
                
                '=== 各セルの文字列と検索語を照合する ===
                For lng_ = lng_L To lng_U
                    For lng_ = lng_L To lng_U
                        Dim str_セル文字列 As String
                        str_セル文字列 = to_検索用文字列( _
                            arr_(lng_, lng_), _
                            ws_対象シート.Cells(rng_使用範囲.Row + lng_ - 1, rng_使用範囲.Column + lng_ - 1))
                        
                        If Len(str_セル文字列) > 0 Then
                            Dim str_検索語 As Variant
                            For Each str_検索語 In col_検索語
                                If InStr(1, str_セル文字列, CStr(str_検索語), vbTextCompare) > 0 Then
                                    
                                    '=== ヒット情報をUIに追記する ===
                                    Dim str_セル番地 As String
                                    str_セル番地 = ws_対象シート.Cells(rng_使用範囲.Row + lng_ - 1, rng_使用範囲.Column + lng_ - 1).Address(False, False)
                                    
                                    Dim str_相対パス As String
                                    str_相対パス = get_相対パス(str_基準フォルダ, str_フルパス)
                                    
                                    Dim str_見出し As String
                                    str_見出し = CStr(ws_対象シート.Cells(1, rng_使用範囲.Column + lng_ - 1).Value)
                                    
                                    ws_UIシート.Cells(lng_次行, 1).Value = str_相対パス
                                    ws_UIシート.Cells(lng_次行, 2).Value = CStr(str_検索語)
                                    ws_UIシート.Cells(lng_次行, 3).Value = ws_対象シート.Name
                                    ws_UIシート.Cells(lng_次行, 4).Value = str_セル番地
                                    ws_UIシート.Cells(lng_次行, 5).Value = str_見出し
                                    
                                    lng_次行 = lng_次行 + 1
                                    lng_ヒット件数 = lng_ヒット件数 + 1
                                End If
                            Next str_検索語
                        End If
                    Next lng_
                Next lng_
            End If
        Next ws_対象シート
        
        '=== 開いたブックを保存せずに閉じる ===
        wb_対象ブック.Close SaveChanges:=False
        Set wb_対象ブック = Nothing
        DoEvents
    Next lng_ファイルIndex
    
CLEANUP:
    '=== エラー時や終了時の後処理を行う ===
    On Error Resume Next
    If Not wb_対象ブック Is Nothing Then wb_対象ブック.Close SaveChanges:=False
    Application.Calculation = xl_計算モード
    Application.EnableEvents = bln_イベント
    Application.ScreenUpdating = bln_画面更新
    On Error GoTo 0
    
    '=== 実行結果をメッセージで通知する ===
    ws_UIシート.Activate
    If lng_ヒット件数 = 0 Then
        MsgBox "検索完了。ヒットはありませんでした。", vbInformation
    Else
        MsgBox "検索完了。ヒット件数: " & lng_ヒット件数, vbInformation
    End If
End Sub

'========================
' Helpers
'========================
Private Function get_UIシート() As Worksheet
    '=== UIシートが無ければ作成し参照を返す ===
    On Error Resume Next
    Set get_UIシート = ThisWorkbook.Worksheets("SearchUI")
    On Error GoTo 0
    If get_UIシート Is Nothing Then
        run_BuildUI
        Set get_UIシート = ThisWorkbook.Worksheets("SearchUI")
    End If
End Function

Private Sub add_UIボタン(ByVal ws_配置先 As Worksheet, ByVal str_表示名 As String, ByVal str_マクロ名 As String, _
                          ByVal str_左上セル As String, ByVal sng_ As Single, ByVal sng_高さ As Single)
    '=== 指定位置にラウンド矩形ボタンを配置する ===
    Dim shp_ボタン As Shape
    On Error Resume Next
    ws_配置先.Shapes(str_表示名).Delete
    On Error GoTo 0
    Set shp_ボタン = ws_配置先.Shapes.AddShape(msoShapeRoundedRectangle, ws_配置先.Range(str_左上セル).Left, ws_配置先.Range(str_左上セル).Top, sng_, sng_高さ)
    With shp_ボタン
        .Name = str_表示名
        .TextFrame2.TextRange.Characters.Text = str_表示名
        .OnAction = "'" & ThisWorkbook.Name & "'!" & str_マクロ名
    End With
End Sub

Private Function get_フォルダ選択パス() As String
    '=== フォルダ選択ダイアログを表示して選択パスを返す ===
    Dim fd_フォルダ As FileDialog
    Set fd_フォルダ = Application.FileDialog(msoFileDialogFolderPicker)
    With fd_フォルダ
        .Title = "基準フォルダを選択"
        .AllowMultiSelect = False
        If .Show = -1 Then
            get_フォルダ選択パス = .SelectedItems(1)
        Else
            get_フォルダ選択パス = ""
        End If
    End With
End Function

Private Function get_検索語コレクション(ByVal ws_UIシート As Worksheet) As Collection
    '=== C5以降の検索語を読み取りコレクション化する ===
    Dim col_ As New Collection
    Dim lng_ As Long, lng_最終行 As Long
    lng_最終行 = ws_UIシート.Cells(ws_UIシート.Rows.Count, "C").End(xlUp).Row
    For lng_ = 5 To lng_最終行
        Dim str_ As String
        str_ = Trim(CStr(ws_UIシート.Cells(lng_, "C").Value))
        If Len(str_) > 0 Then col_.Add str_
    Next
    If col_.Count = 0 Then
        Set get_検索語コレクション = Nothing
    Else
        Set get_検索語コレクション = col_
    End If
End Function

Private Sub collect_Excelファイル再帰(ByVal str_基準 As String, ByRef col_ファイル As Collection)
    '=== 指定フォルダ以下からExcelファイルを再帰的に収集する ===
    Dim str_ファイル名 As String, str_サブ As String
    
    str_ファイル名 = Dir(str_基準 & "\*.xls*")
    Do While Len(str_ファイル名) > 0
        If Left(str_ファイル名, 2) <> "~$" Then col_ファイル.Add str_基準 & "\" & str_ファイル名
        str_ファイル名 = Dir
    Loop
    
    str_サブ = Dir(str_基準 & "\*", vbDirectory)
    Do While Len(str_サブ) > 0
        If str_サブ <> "." And str_サブ <> ".." Then
            If (GetAttr(str_基準 & "\" & str_サブ) And vbDirectory) = vbDirectory Then
                collect_Excelファイル再帰 str_基準 & "\" & str_サブ, col_ファイル
            End If
        End If
        str_サブ = Dir
    Loop
End Sub

Private Function get_相対パス(ByVal str_基準 As String, ByVal str_フルパス As String) As String
    '=== 基準フォルダからの相対パスを求める ===
    Dim str_基準末尾付 As String: str_基準末尾付 = str_基準
    If Right(str_基準末尾付, 1) <> "\" Then str_基準末尾付 = str_基準末尾付 & "\"
    If StrComp(Left(str_フルパス, Len(str_基準末尾付)), str_基準末尾付, vbTextCompare) = 0 Then
        get_相対パス = Mid(str_フルパス, Len(str_基準末尾付) + 1)
    Else
        get_相対パス = str_フルパス
    End If
End Function

Private Function get_使用範囲_空対応(ByVal ws_対象 As Worksheet) As Range
    '=== 使用範囲が空のシートはNothingを返す ===
    If Application.WorksheetFunction.CountA(ws_対象.Cells) = 0 Then
        Set get_使用範囲_空対応 = Nothing
    Else
        Set get_使用範囲_空対応 = ws_対象.UsedRange
    End If
End Function

'========================
' Value normalization
'========================
Private Function to_検索用文字列(ByVal v As Variant, Optional ByVal rng_元セル As Range = Nothing) As String
    '=== セル値を安全に文字列化する(Null/CVErr/Empty対策と日付の書式統一) ===
    If IsError(v) Or IsNull(v) Or IsEmpty(v) Then
        to_検索用文字列 = ""
        Exit Function
    End If
    
    '=== 日付っぽいかを判定して表示用書式に統一する ===
    Dim bln_日付形式 As Boolean
    If Not rng_元セル Is Nothing Then
        Dim str_書式 As String: str_書式 = LCase$(CStr(rng_元セル.NumberFormatLocal))
        bln_日付形式 = (InStr(str_書式, "y") > 0 Or InStr(str_書式, "m") > 0 Or InStr(str_書式, "d") > 0)
    Else
        bln_日付形式 = IsDate(v)
    End If
    
    If bln_日付形式 Then
        to_検索用文字列 = Format$(v, "yyyy-mm-dd")
    Else
        to_検索用文字列 = CStr(v)
    End If
End Function

タグ: Excel VBA 業務効率化 ツール作成 全文検索

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?