文字列検索はFindメソッドよりLike演算子を使おう
VBAを勉強している方とミーティングをしている際こんな質問をいただきました。
「ワークシート内から特定の文字列を検索する際になぜFindメソッドを使わないのか?」という質問です。
私は特定の文字列や数値の検索時にFindメソッドを使いません。
ワイルドカードを含めたLike演算子を採用します。理由はこの2つです。
- Findメソッドの戻り値であるRangeオブジェクトの扱いが難しいから(オブジェクト変数の使い方)
- Like演算子の方が「簡単」かつ「速い」から
しかし初心者様の多くはFindメソッドを採用する様です。
セルの中にある文字列や数値を検索する方法を調べる時のキーワードが重要です。
ネット検索時に「VBA 文字列 検索」に近いキーワードで検索していませんか?
「文字列を検索・・・」の様なキーワードでインターネット検索を行うと「Findメソッド」関連の記事が頻出します。
このような背景もあり文字列検索=Findメソッドという環境が作られやすいのですが実はもっと簡単な方法があります。
文字列を検索する際は対象の文字列との「完全一致」や「部分一致」を探す事ができるLike演算子をおすすめします。
本記事ではFindメソッドの問題点を整理したうえでより簡単なLike演算子を使ったコードを紹介します。
関連記事
Findメソッドを完全一致で使う方法を解説しています。
完全一致で検索【検索】Findメソッドを使って完全一致で文字列を探す方法|VBA
ネット検索で「Findメソッド」を検索するとInstr関数の記事も同じぐらいヒットします。
Instr関数は任意の文字列内から特定文字を探すという関数です。以下記事ではInstrRev関数と共に解説しています。
Instr関数の使い方InStrRev関数がうまくいかない時は戻り値の読み方を見直す
文字列の検索時に対象文字との「完全一致」や「部分一致」を探す方法です。Like演算子を使います。
文字列を探すVBAで文字列を比較Like演算子とワイルドカードで完全/部分一致を確認【一覧で表示】
関連書籍
こちらの書籍の中ではAutoFilter(フィルタ)や配列と一緒にLike演算子を使っています。
おすすめ電子書籍(Kindleストア)はこちらです。私一部の書籍は電子書籍で購入しています。
電子書籍をVBEと同じ画面で横に並べながら作業する事で作業効率が劇的にあがりますよ。
Findメソッドとは
まずはFindメソッドについて簡単におさらいしておきましょう。Microsoftのドキュメントから定義を引用します。
セル範囲内で特定の情報を検索します。
Nicrosoft:Range.Find メソッド (Excel) より抜粋
関連記事(リンク)を見ていただければ構文や引数(LookIn、LookAtなど)を見る事ができます。
本記事は「Findメソッドを積極的に使おう」という記事ではないので説明はこのぐらいに省略して先に進みます。
Findメソッドを使ってみる
定義に続きMicrosoftのドキュメントからデータ(コード)を引用します。
Nicrosoft:Range.Find メソッド (Excel) より抜粋Sub FindValue() Dim c As Range Dim firstAddress As String With Worksheets(1).Range("A1:A500") 'セルの番地を指定 Set c = .Find(2, lookin:=xlValues) If Not c Is Nothing Then firstAddress = c.Address Do c.Value = 5 'セルに 5 を代入(入力) Set c = .FindNext(c) Loop While Not c Is Nothing End If End With End Sub
色々難しい要素が含まれたプロシージャですがひとまず先に結論を見ましょう。
こちらのプロシージャを実行するとセルA1~A500の中にある「2」という文字を含んだセルの文字列が5に変わります。
部分一致も完全一致も関係なく「2」を見つけて「5」に変えてしまうというコードです。
このコードなら検索したいキーワードが「ないのに見つからない」という状態も回避できますね。
対象文字列「2」を発見後何らかの処理が出来ています。特定文字は認識できているので一見問題なさそうです。
ただしこのプロシージャをパッと見た時に「分かりにくい」、「ちょっと難しい」と思いませんでしたか?
比較的シンプルに書いている様に見えますが色々な要素が組み込まれているので初心者様では理解できないです。
ではなぜこのプロシージャが難しいのかを考えてみます。考えられる要因のひとつは「Findメソッドの戻り値」です。
続いてFindメソッドの戻り値について深掘りしていきましょう。
Findメソッドを使ったコードの問題点
問題点は1つです。「Findメソッドの戻り値がRangeオブジェクト」という事です。
さらに具体的に書くと「Rangeオブジェクトを取り回す準備をする事でプロシージャが複雑になっている」のが問題です。
戻り値=「Rangeオブジェクト」の弊害
問題点を深堀りしましょう。Rangeオブジェクトを使う事による3つのデメリットを紹介します。
ここて提示する3つのポイントは私がFindメソッドを採用しない理由そのものです。
1_オブジェクト変数を用意する必要がある
メソッド実行後に返ってくる値(Rangeオブジェクト)を受ける体制が必要です。
Rangeオブジェクトを受ける為に変数を1つ用意する必要があります。ただし通常の変数ではないです。
変数にはRangeオブジェクトを格納する事になるのでオブジェクト変数を用意する必要があります。
具体的な使い方としては「Set」というコードを使って変数に値を格納する事になります。
「なんでこの変数はSetというコードが必要なの?」と思った事がある方は多いのではないでしょうか。
オブジェクト変数についての説明はこちらをご覧ください。Setを使う理由が分かります。
この様にオブジェクト変数が理解出来てないとFindメソッドは使いにくいというのが1つ目のデメリットです。
2_FindNextメソッドと一緒に使わないと実用性がない
複数のキーワードに対してFindメソッドを使うとなるとFindNextメソッドも併用する必要があります。
FindNextメソッドも初心者のうちは非常に難しい処理になります。(FindNextメソッドの使い方はこちら)
難しい理由は実行時の戻り値を見る事ができないと何をやっているのか全く分からないからです。
FindNextメソッドを理解する為にはローカルウインドウの使い方を理解する必要があります。
加えてFindNextメソッドの戻り値で得たセルへの処理の為にDo ~ Loop Whileステートメントも使う事になります。
FindNextメソッド単体では周回構造(ループ)を構築する事ができない為です。
FindNextメソッドなど複数の要素と一緒に使用しないと実用性が無いという事が2つ目のデメリットです。
3_配列との相性が悪い
3つ目は中~上級者様向けのデメリットになります。配列を使える方が対象になります。
端的に申し上げますと「配列に格納された値にはFindメソッドが使えない」です。
Rangeオブジェクトに属するFindメソッドはセルに用意された値に対して有効になります。
代わって配列に格納された値はRangeオブジェクトやCellsプロパティとの関係性が無い状態になっています。
という事は配列に格納された値はFindメソッドの戻り値になり得ないです。
配列と組み合わせて使うのが難しいというのが3つ目のデメリットです。
3つのデメリットへの対策
3つのデメリットに対して対策します。方向性としてこの3つのキーワードを目標に改善を進めます。
- オブジェクト変数を使わないコード
- 「かんたんで分かりやすい」かつ「シンプル」な構造
- 配列とも相性が良い仕様
これらを考慮してプロシージャを用意していきます。
Like演算子を使ったプロシージャを用意する
サンプルマクロを自身で考えて用意しました。
Findメソッドを紹介する際に使用したものと同じワークシートの環境で以下プロシージャを実行してください。
Findメソッドを使ったプロシージャと同じ実行結果を得ることができます。
配列は難しいので一旦外してプロシージャを用意しています(記事後半で別途プロシージャを紹介しています)
Sub Findメソッドを使わずに文字列を検索する_1()
Dim kw As String 'keyword:検索対象文字を格納する変数
Dim r As Long 'row:For~Nextステートメントで使う変数
kw = 2 '検索対象文字は 2
'A列の1行目から最終行までループ
For r = 1 To Cells(Rows.Count, 1).End(xlUp).Row
With Worksheets(1)
'変数rを使って指定したセルに検索対象文字は含まれているか?
If .Cells(r, 1) Like "*2*" Then
'条件Trueの際はセルの値を5に上書きする
.Cells(r, 1) = 5
End If
End With
Next
End Sub
オブジェクト変数は使っていません。
加えてFor~NextステートメントとIfステートメント、Like演算子というシンプルな構造にしています。
コードのポイントと解説記事の紹介
紹介したプロシージャは以下要素で構築できます。このプロシージャの方がFindメソッドを使うよりかんたんです。
各要素についての解説(リンク記事)を用意しておきましたので必要に応じてご覧ください。
参考:配列を使ってさらに改善を進めました
参考で配列を使ったプロシージャも掲載しておきます。
ユーザー目線で見ると配列を使わないコードより少し難しくなりますが基本構造は同じです。
検索数が数万~10万を超えるようになると配列の方が格段に速く処理を進める事ができる様になります。
Sub Findメソッドを使わずに文字列を検索する_2()
Dim kw As String 'keyword:検索対象文字を格納する変数
Dim r As Long 'row:A列の最終行を格納する変数
Dim ary() As Variant 'array:配列
Dim i As Long 'index:For~Nextステートメントで使う変数
kw = 2 '検索対象文字は 2
r = Cells(Rows.Count, 1).End(xlUp).Row 'A列の最終行を取得
ary = Range(Cells(1, 1), Cells(r, 1)) '配列に値を格納
'配列の最初の要素から最後の要素までループ
For i = LBound(ary) To UBound(ary)
'変数iを添え字として使い指定した配列に検索対象文字は含まれているか?
If ary(i, 1) Like "*2*" Then
'条件Trueの際は配列内の値を5に上書きする
ary(i, 1) = 5
End If
Next
Range(Cells(1, 1), Cells(r, 1)) = ary '配列の値ををセルに戻す
End Sub
配列について知りたい方はこちらの記事をご覧ください。
高速処理を実現【検索にも使える】VBAの2次元配列で作業の高速化を実現させる
まとめ
文字列を検索する際はFindメソッドに頼らずにLike演算子とワイルドカードを使う事をおすすめします。
Findメソッドを使うよりかんたんに文字列を検索できる様になります。
加えてLike演算子を使いこなせるようになると正規表現を習得する必要がほぼ無くなります。
理由はLike演算子とワイルドカードの組み合わせは正規表現で対応できる範囲の大半をカバーできるからです。
正規表現を習得するよりもLike演算子の方がかんたんです。エラーも特定しやすくなるのでエラー対策にもなります。
さらに付け加えるとLike演算子とワイルドカードを使いこなせばVLOOKUP関数のような抽出もできる様になります。
色々な使い方ができる万能な演算子(機能)ですので是非ご使用ください。