Excelのセルに「営業」と書いてあるのに、VBAの`Range.Find`が見つけられない。そんなときは、検索対象(LookIn)、一致方法(LookAt)、書式検索(SearchFormat)を確かめましょう。`Find`は以前の設定を引き継ぐことがあるため、毎回明示するのが安全です。
結論:Findの検索条件を省略しない
`Range.Find`は指定範囲から値や文字列を探すExcel VBAのメソッドです。Microsoft Learnによると、`LookIn`・`LookAt`・`SearchOrder`・`MatchByte`などの設定は次の検索へ保存されます。Excelの検索ダイアログとも設定を共有するため、コードを変えていなくても結果が変わることがあります。
| 項目 | 意味 | 例 |
|---|---|---|
| LookIn | どこを見るか | xlValues:値、xlFormulas:数式 |
| LookAt | 完全一致か部分一致か | xlWhole / xlPart |
| SearchFormat | セル書式を条件にするか | False:書式条件を使わない |
| MatchCase | 大文字小文字を区別するか | False:区別しない |
用語解説:Nothing
VBAでオブジェクトが見つからない、またはセットされていないことを表す特別な値。Findが失敗したときに返るため、使う前に確認します。
3行だけのサンプルで実行する
Excelの新しいブックを開き、シート名を`Sheet1`にして、A2に「営業」、A3に「営業部」、A4に「開発」と入力します。マクロが使えるデスクトップ版Excelで標準モジュールへ次のコードを貼ります。既存の重要なブックでは試さないでください。
Option Explicit
Sub FindExactSales()
Dim ws As Worksheet
Dim found As Range
Set ws = ThisWorkbook.Worksheets("Sheet1")
Set found = ws.Range("A2:A4").Find( _
What:="営業", _
After:=ws.Range("A4"), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
MatchByte:=False, _
SearchFormat:=False)
If found Is Nothing Then
Debug.Print "見つかりません"
Else
Debug.Print found.Address(False, False)
End If
End Sub
VBAエディターのイミディエイトウィンドウ(Ctrl+G)で、想定出力は`A2`です。`LookAt:=xlWhole`なので「営業部」とは一致させません。`After`は検索開始位置の直前に当たるセルで、指定したセルそのものは折り返したあとに検索されます。
デスクトップ版Excelやマクロの実行権限がない環境では実行できません。セキュリティ警告を回避するための設定変更はせず、組織の運用ルールに従ってください。
完全一致と部分一致の違いを確認
同じ範囲で`LookAt:=xlPart`へ変更すれば、「営業」と「営業部」が両方候補になります。ただし`Find`は最初に見つけた1セルだけを返します。すべての結果が必要なら`FindNext`などで繰り返します。
Set found = ws.Range("A2:A4").Find( _
What:="営業", LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False, _
MatchByte:=False, SearchFormat:=False)
`xlValues`はセルに評価された値を対象にします。一方、数式そのものの文字列を調べたい場合は`xlFormulas`を選びます。「画面で値が見えるのに一致しない」場合、どちらを探しているかが重要です。
Nothingを確認してエラーを防ぐ
見つからなかったとき、`found.Address`のように直接プロパティを読もうとするとエラーになります。先に`Is Nothing`で判定してください。次は検索文字を「存在しない語句」に変えた例です。
Set found = ws.Range("A2:A4").Find( _
What:="経理", LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False, _
MatchByte:=False, SearchFormat:=False)
If found Is Nothing Then
Debug.Print "対象なし"
Else
Debug.Print found.Address(False, False)
End If
この例の想定出力は`対象なし`です。セルが`Nothing`になったあとで`.Address`へ進むコードを書かないことがポイントです。
まだ見つからない場合のチェック表
| 確認点 | 起こりやすい問題 | 対処 |
|---|---|---|
| 検索範囲 | A2:A4の外を探している | 対象範囲とSheet名を確認 |
| LookIn | 数式を探すつもりが値を指定 | 目的に応じxlValues/xlFormulasを指定 |
| LookAt | 部分一致が必要なのにxlWhole | xlPartへ変更 |
| SearchFormat | 以前の書式条件が残る | Falseを明示し書式検索を無効化 |
| データ | 余計な空白や改行が含まれる | セルの文字列を確認 |
| 大文字小文字 | 想定外に区別される | MatchCaseを明示 |
`SearchFormat:=False`を指定しても「文字列が空白を含む」問題は直りません。まずセルの内容と検索条件を1つずつ揃え、条件を変えたら再実行して差を比べましょう。
複数ヒットのFindNextでは折り返しに注意
`Find`は検索範囲の末尾で先頭に戻るため、`FindNext`の繰り返しでは最初のセルのアドレスを覚えて停止します。次は先ほどの3行を探し、2件のセル番地を表示する例です。
Dim firstAddress As String
Set found = ws.Range("A2:A4").Find( _
What:="営業", LookIn:=xlValues, LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, MatchByte:=False, SearchFormat:=False)
If Not found Is Nothing Then
firstAddress = found.Address
Do
Debug.Print found.Address(False, False)
Set found = ws.Range("A2:A4").FindNext(found)
If found Is Nothing Then Exit Do
Loop While found.Address <> firstAddress
End If
想定される2つのセルはA2とA3です。`FindNext`で先頭に戻るところまで探したら止めます。途中で見つからず`Nothing`になった場合も、先に退出するようにしています。
関連する学習と次の一手
VBAに慣れていない場合は、セルを指定する`Range`と`Cells`の違いを復習すると、検索範囲の見落としを減らせます。今回の記事はFindの引数を固定し、見つからない問題を切り分けることに絞りました。
(VBAでセルを指定する方法については『VBA RangeとCellsの使い分け判断マップ』をご参照ください)
(VBAの学習背景については『SES現場でVBAできますか?未経験向けExcelマクロ実践術』をご参照ください)
SearchFormatはTrueにすれば見つかりやすくなりますか?
いいえ。Trueはセル書式を検索条件に含める用途です。通常の値検索ではFalseを明示する方が予期しない絞り込みを避けられます。
検索対象が複数ある場合、Findだけで全部返りますか?
Findが返すのは最初の一致セルです。FindNextを使い、最初のセルに戻ったら終了するようにします。
まず3行の表でxlWholeとxlPartを切り替え、結果が変わることを自分のExcel環境で確認してみてください。