アフィリエイト・アドセンス広告を利用しています。詳細は こちら



Excel VBA - 配列と Replace 関数を使った文字列置検索(部分一致)置換処理メモ

Excel VBA で配列と Replace 関数を使って文字列を検索(部分一致)・置換する VBA コードを公開します。

前回の記事 では検索対象の文字列に対して完全一致したものだけ置換しましたが、今回は部分一致した文字列を置換する記事内容となっています。

Excel と Excel Visual Basic Editor(VBE)の環境設定については 以前公開した記事 より、VBA コードの基本的な内容については こちらの記事 よりご確認ください。

Excel VBA - 配列と Replace 関数を使った文字列置検索(部分一致)置換処理メモ


配列と Replace 関数を使った文字列置検索(部分一致)置換用サンプルファイル(xlsx ファイル)

配列と Replace 関数を使った文字列置検索(部分一致)置換用のサンプルファイル(xlsx ファイル)を公開します。

サンプルデータは Start Point さんのところで公開しているコピペで使える都道府県一覧リスト・県庁所在地一覧 を加工しています。

こちらで作成した 文字列置換前置換後A.xlsx ファイル文字列置換前置換後B.xlsx ファイル を用意しました。ファイル名リンクをクリックすると Google ドライブからダウンロードするようにしています。

文字列置換前置換後A.xlsx ファイル は VBA コードを実行する前の状態、文字列置換前置換後B.xlsx ファイル は VBA コード実行後の処理結果内容となっています。

いずれも xlsx ファイルとなっており VBA コードは含まれていません。VBA コードを実行するにはファイル拡張子が xlsm(マクロ有効ブック)となっている必要がありますが、xlsm 形式での配布は念のため避けています。

VBA コードを実行するには 文字列置換前置換後A.xlsx ファイル をダウンロード後 xlsm 形式に保存、以降各セクションで紹介している VBA コードを各自追加して実行してもらう形としています。

VBA コードの追加方法は VBE のプロジェクトエクスプローラー(Ctrl + R キー)画面にて、開いている VBAProject(ファイル名.xlsm)→「標準モジュール」を開き、右クリックから「挿入」→「標準モジュール」でクリック、追加した標準モジュールに VBA コードを貼り付けることで動作できるようにしています。

一部 VBA コード内にある Call ステートメントを使った Function プロシージャの呼び出し については、呼び出し先標準モジュールのプロパティ(F4 キー)画面から(オブジェクト名)欄を設定するのが前提となっています。

セル検索(部分一致)置換処理については指定した文字列があるセル内容を、配列と Replace 関数を組み合わせて部分一致で検索して置換する VBA コードを公開します。VBA コードと詳細な内容については 次のセクション で説明します。

配列に格納した全データの部分一致による逐次検索置換処理のため、大量のデータ件数を用いた場合は処理速度に大きな影響を受ける可能性があります。

配列と Replace 関数を使った文字列置検索(部分一致)置換処理 VBA サンプルコード

以下、配列と Replace 関数を使った文字列置検索(部分一致)置換処理 VBA サンプルコードです。

この VBA コードはシート「文字列置換前置換後シート」の地方区分単位(A 列)で、1つのセルにセル内改行でまとめられた B 列(都道府県)と C 列(県庁所在地)の各セルを 2次元配列に格納します。別シート「都道府県文字列置換リスト」と「県庁所在地文字列置換リスト」であらかじめ用意した検索文字列と置換文字列も 2次元配列に格納して、2次元配列に格納した文字列の検索(部分一致)と置換を Replace 関数を使って行うものとなっています。

Replace 関数を使って置換対象文字列の検索と置換(ここでは日本語名から英語名に置換)を行い、その結果を D 列(都道府県)と E 列(県庁所在地)に反映します。

この配列と Replace 関数を使った置換方法は こちらのサイト で紹介している配列を使用した高速置換 VBA コードと内容は同じです。

対象の文字列が置換できているかどうか確認できるように、元のセルへ上書きはせず隣の空きセルに転記しています。反映先のセルがわかりやすいように、あらかじめ該当セルを罫線で囲んでいます。

セル検索(部分一致)置換処理部分以外の基本的な VBA コードについては 以前公開した記事内容 を参照してください。

次のセクション では配列と Replace 関数を使った文字列置検索(部分一致)置換処理がある VBA コード部分について内容を説明します。

2023/11/5 追記

以下の VBA コードにはメモリリークを起こす可能性があります。詳細は こちら

こちら でメモリリークを対策した VBA コードを公開します。

Option Explicit

Sub InStrReplaceCharMergeRows()
  ' シート「文字列置換前置換後シート」で区切り文字で連結されたセル内文字列を、
  ' InStr 関数と Replace 関数を使ってシート「都道府県文字列置換リスト」「県庁所在地文字列置換リスト」にある文字列を検索して置換

  ' 実行速度計測開始
  Dim starttime As Double
  starttime = Timer

' ----------

  ' カウンタ用変数(一見見分けにくい文字同士を組み合わせない形、i と j、m と n、o(オー) と 0(ゼロ))
  Dim i As Long, k As Long

' ----------

  ' シート格納用オブジェクト変数
  Dim ws1 As Worksheet, ws2 As Worksheet, ws3 As Worksheet, ws4 As Worksheet

  ' オブジェクト変数にシートセット
  Set ws1 = ThisWorkbook.Worksheets("文字列置換前置換後シート") ' 文字列検索先シートをオブジェクト変数にセット
  Set ws2 = ThisWorkbook.Worksheets("都道府県文字列置換リスト") ' 文字列検索元シートをオブジェクト変数にセット
  Set ws3 = ThisWorkbook.Worksheets("県庁所在地文字列置換リスト") ' 文字列検索元シートをオブジェクト変数にセット
'  Set ws4 = ThisWorkbook.Worksheets("temp") ' データ出力先テスト用シート

' ----------

  ' 最終行取得用変数
  Dim maxrow1 As Long, maxrow2 As Long, maxrow3 As Long
  
  ' シート最終行取得して変数に格納
  maxrow1 = ws1.Cells(Rows.Count, "A").End(xlUp).Row
  maxrow2 = ws2.Cells(Rows.Count, "A").End(xlUp).Row
  maxrow3 = ws3.Cells(Rows.Count, "A").End(xlUp).Row

  ' 最終列取得用変数
  Dim maxcol1 As Long, maxcol2 As Long, maxcol3 As Long

  ' 各シートの最終列取得して変数に格納
'  maxcol1 = ws1.Cells(1, Columns.Count).End(xlToLeft).Column
  maxcol1 = 3
  maxcol2 = ws2.Cells(1, Columns.Count).End(xlToLeft).Column
  maxcol3 = ws3.Cells(1, Columns.Count).End(xlToLeft).Column

' ----------

  ' シートの指定したセル範囲を配列として格納する動的配列
  Dim data1() As Variant, data2() As Variant, data3() As Variant

  ' シートの指定した範囲内セル(キーワード列)を配列として動的配列に格納
  data1 = ws1.Range(ws1.Cells(2, 2), ws1.Cells(maxrow1, maxcol1)).Value ' 文字列置換前置換後シート最終行・最終列までを Range で範囲指定、配列として動的配列にセット
  data2 = ws2.Range(ws2.Cells(2, 1), ws2.Cells(maxrow2, maxcol2)).Value ' 都道府県文字列置換リストシート最終行・最終列までを Range で範囲指定、配列として動的配列にセット
  data3 = ws3.Range(ws3.Cells(2, 1), ws3.Cells(maxrow3, maxcol3)).Value ' 県庁所在地文字列置換リストシート最終行・最終列までを Range で範囲指定、配列として動的配列にセット

  ' Range オブジェクトを受け取り、2次元配列で返す自作関数 GetArrFromRange
'  data1 = GetArrFromRange.GetArrFromRange(ws1.Range(ws1.Cells(2, 2), ws1.Cells(maxrow1, maxcol1)))
'  data2 = GetArrFromRange.GetArrFromRange(ws2.Range(ws2.Cells(2, 1), ws2.Cells(maxrow2, maxcol2)))
'  data3 = GetArrFromRange.GetArrFromRange(ws3.Range(ws3.Cells(2, 1), ws3.Cells(maxrow3, maxcol3)))

' ----------

  ' Instr 関数で検索文字列を検索、Replace 関数でセル内にある検索文字列を置換文字列に置換(都道府県文字列置換リスト)
  For i = LBound(data1, 1) To UBound(data1, 1) ' 配列 data1 の 1次元最大要素までループ処理
    For k = LBound(data2, 1) To UBound(data2, 1) ' 配列 data2 の 1次元最大要素までループ処理
'      If InStr(data1(i, 1), data2(k, 1)) > 0 Then ' Instr 関数で配列 data1 の i 行 1列目セル内文字列の中から、配列 data2 の k 行 1列目で指定した文字列を検索して位置を返す
        ' If 文で InStr 関数で文字列を検索して位置を返した場合(> 0)
        data1(i, 1) = Replace(data1(i, 1), data2(k, 1), data2(k, 2)) ' 配列 data1 の i 行 1列目セル内文字列から配列 data2 の k 行 1列目の文字列を検索、配列 data2 の k 行 2列目の文字列に置換して配列 data1 の i 行 1列目に代入
'      End If
    Next k
  Next i

  ' Instr 関数で検索文字列を検索、Replace 関数でセル内にある検索文字列を置換文字列に置換(県庁所在地文字列置換リスト)
  For i = LBound(data1, 1) To UBound(data1, 1) ' 配列 data1 の 1次元最大要素までループ処理
    For k = LBound(data3, 1) To UBound(data3, 1) ' 配列 data3 の 1次元最大要素までループ処理
'      If InStr(data1(i, 2), data3(k, 1)) > 0 Then ' Instr 関数で配列 data1 の i 行 2列目セル内文字列の中から、配列 data3 の k 行 1列目で指定した文字列を検索して位置を返す
        ' If 文で InStr 関数で文字列を検索して位置を返した場合(> 0)
        data1(i, 2) = Replace(data1(i, 2), data3(k, 1), data3(k, 2)) ' 配列 data1 の i 行 2列目セル内文字列から配列 data3 の k 行 1列目の文字列を検索、配列 data3 の k 行 2列目の文字列に置換して配列 data1 の i 行 2列目に代入
'      End If
    Next k
  Next i

' ----------

  ' 二次元配列 data2 の内容を、Range で指定したセルから Resize で範囲を変更してセルに代入
  ws1.Range("D2").Resize(UBound(data1, 1), UBound(data1, 2)).Value = data1
  ws1.Activate

  ' テスト用シートに出力する場合は以下のテスト用シートを格納したオブジェクト変数に変更
'  ws4.Range("D2").Resize(UBound(data1, 1), UBound(data1, 2)).Value = data1
'  ws4.Activate

' ----------

  ' 実行速度計測結果表示
  Debug.Print Format(Timer - starttime, "0.00秒")

End Sub

2次元配列に格納した文字列を Replace 関数で検索(部分一致)と置換

  ' Instr 関数で検索文字列を検索、Replace 関数でセル内にある検索文字列を置換文字列に置換(都道府県文字列置換リスト)
  For i = LBound(data1, 1) To UBound(data1, 1) ' 配列 data1 の 1次元最大要素までループ処理
    For k = LBound(data2, 1) To UBound(data2, 1) ' 配列 data2 の 1次元最大要素までループ処理
'      If InStr(data1(i, 1), data2(k, 1)) > 0 Then ' Instr 関数で配列 data1 の i 行 1列目セル内文字列の中から、配列 data2 の k 行 1列目で指定した文字列を検索して位置を返す
        ' If 文で InStr 関数で文字列を検索して位置を返した場合(> 0)
        data1(i, 1) = Replace(data1(i, 1), data2(k, 1), data2(k, 2)) ' 配列 data1 の i 行 1列目セル内文字列から配列 data2 の k 行 1列目の文字列を検索、配列 data2 の k 行 2列目の文字列に置換して配列 data1 の i 行 1列目に代入
'      End If
    Next k
  Next i

  ' Instr 関数で検索文字列を検索、Replace 関数でセル内にある検索文字列を置換文字列に置換(県庁所在地文字列置換リスト)
  For i = LBound(data1, 1) To UBound(data1, 1) ' 配列 data1 の 1次元最大要素までループ処理
    For k = LBound(data3, 1) To UBound(data3, 1) ' 配列 data3 の 1次元最大要素までループ処理
'      If InStr(data1(i, 2), data3(k, 1)) > 0 Then ' Instr 関数で配列 data1 の i 行 2列目セル内文字列の中から、配列 data3 の k 行 1列目で指定した文字列を検索して位置を返す
        ' If 文で InStr 関数で文字列を検索して位置を返した場合(> 0)
        data1(i, 2) = Replace(data1(i, 2), data3(k, 1), data3(k, 2)) ' 配列 data1 の i 行 2列目セル内文字列から配列 data3 の k 行 1列目の文字列を検索、配列 data3 の k 行 2列目の文字列に置換して配列 data1 の i 行 2列目に代入
'      End If
    Next k
  Next i

2次元配列 data1・data2・data3 に格納した文字列と Replace 関数を使って検索と置換処理をします。途中 InStr 関数を含んだ If 文ありますが、なくても Replace 関数だけで置換処理が可能なためコメントアウトしています。InStr 関数については後述。

2次元配列 data1 には置換対象となるシート「文字列置換前置換後シート」の B・C 列の都道府県・県庁所在地セルを格納、2次元配列 data2 にはシート「都道府県文字列置換リスト」にセットした A・B 列の検索文字と対の置換文字を格納、2次元配列 data3 にはシート「県庁所在地文字列置換リスト」にセットした A・B 列の検索文字と対の置換文字を格納しています。

64~71 行目で都道府県を日本語から英語に置換、74~81 行目で県庁所在地を同じように置換する処理に分けています。検索・置換で参照している 2次元配列が違うだけで(2次元配列 data2 と data3)で処理の流れ・コードは同じです。

64行目は For 文とカウンタ変数 i と LBound・UBound 関数を使って、置換対象の文字列を格納した 2次元配列 data1 の 1次元最大要素(行相当)までループ処理、次の 65行目では For 文とカウンタ変数 k と LBound・UBound 関数を使って、検索文字列と置換文字列を格納した 2次元配列 data2 の 1次元最大要素(行相当)までループ処理をします。

68行目で Replace 関数に引数を指定してカウンタ変数分の置換を処理します。

第 1引数に置換の対象となる文字列を格納した 2次元配列(data1 の i(カウンタ変数)行 1列目)を指定、第 2引数に検索する文字列を格納した 2次元配列(data2 の k(カウンタ変数)列 1列目)を指定、第 3引数に置換する文字列を格納した 2次元配列(data2 の k(カウンタ変数)列 2列目)を指定します。

Replace 関数での処理結果を、Replace 関数の第 1引数で指定した 2次元配列(data1 の i(カウンタ変数)行 1列目)に代入することで置換することができます。検索文字列が見つからなければ置換なしで、2次元配列 data1 の i(カウンタ変数)行 1列目は変更なしとなります。

74~81 行目のコードは 64~71 行目と処理の流れはまったく同じです。違う点は 2次元配列 data2(都道府県検索・置換文字列)から 2次元配列 data3(県庁所在地検索・置換文字列)に変更しているだけとなっています。

以上が配列と Replace 関数を使った検索・置換処理の流れになります。

66行目及び 76行目で InStr 関数を使った If 文を使用しています。

これは InStr 関数の第 1引数に検索の対象となる文字列を格納した 2次元配列(data1 の i(カウンタ変数)行 1列目)を指定、第 2引数に 第 1引数の中から検索する文字列を格納した 2次元配列(data2 の k(カウンタ変数)列 1列目)を指定しています。

この InStr 関数を使うことで 第 1引数の 2次元配列 data1 で指定した文字列の中から、第 2引数の 2次元配列 data2で指定した文字列を検索して、最初に見つかった文字位置を返します。

これを If 文で 0 以上(> 0)であるかどうか判定させることで、置換対象の文字列があるかどうか調べることができます。ただ、今回の場合では Replace 関数で必要な処理が足りているため、InStr 関数を使わなくても置換できるようになっています。検索と置換が違う複数の条件がある場合に、InStr 関数と Replace 関数を組み合わせて使うことになるでしょう。

ちなみに InStr 関数のコメントアウトを外しても処理結果は同じになります。

Excel VBA コードメモリリーク対策について

X(Twitter) にて教えてもらいましたが、LBound と UBound 関数を使用した VBA コードの書き方によってはメモリリークが起きる可能性があるようです。

ただ、指摘のあった関数による VBA コードのメモリリークについて日本語で言及している情報が少なく、今回公開した VBA コードが原因でメモリリークが起きるかどうか確認できていません。(こちらで単純にメモリリークに気づいていないというのもあります)

X(Twitter) で紹介してもらった こちらのサイト ではメモリリークする・しないコードの書き方が紹介されています。以下各セクションではこの書き方にならって、コード内容を書き直したメモリリーク対策版 VBA コードを公開します。

メモリリークが起きないとされているコード内容に修正しただけなので、実際にメモリリークが起きないかどうかまでは確認していません。

基本的に関数の返り値を一度変数に格納して、その変数を使用する方法に書き換えた内容となっています。各コードについて元の VBA コードから追加・変更した箇所をハイライトで表示しています。

配列と Replace 関数を使った文字列置検索(部分一致)置換処理 VBA サンプルコード メモリリーク対策版

Option Explicit

Sub InStrReplaceCharMergeRowsFixMemoryLeaks1() ' メモリリーク対策版
  ' シート「文字列置換前置換後シート」で区切り文字で連結されたセル内文字列を、
  ' InStr 関数と Replace 関数を使ってシート「都道府県文字列置換リスト」「県庁所在地文字列置換リスト」にある文字列を検索して置換

  ' 実行速度計測開始
  Dim starttime As Double
  starttime = Timer

' ----------

  ' カウンタ用変数(一見見分けにくい文字同士を組み合わせない形、i と j、m と n、o(オー) と 0(ゼロ))
  Dim i As Long, k As Long

' ----------

  ' シート格納用オブジェクト変数
  Dim ws1 As Worksheet, ws2 As Worksheet, ws3 As Worksheet, ws4 As Worksheet

  ' オブジェクト変数にシートセット
  Set ws1 = ThisWorkbook.Worksheets("文字列置換前置換後シート") ' 文字列検索先シートをオブジェクト変数にセット
  Set ws2 = ThisWorkbook.Worksheets("都道府県文字列置換リスト") ' 文字列検索元シートをオブジェクト変数にセット
  Set ws3 = ThisWorkbook.Worksheets("県庁所在地文字列置換リスト") ' 文字列検索元シートをオブジェクト変数にセット
'  Set ws4 = ThisWorkbook.Worksheets("temp") ' データ出力先テスト用シート

' ----------

  ' 最終行取得用変数
  Dim maxrow1 As Long, maxrow2 As Long, maxrow3 As Long
  
  ' シート最終行取得して変数に格納
  maxrow1 = ws1.Cells(Rows.Count, "A").End(xlUp).Row
  maxrow2 = ws2.Cells(Rows.Count, "A").End(xlUp).Row
  maxrow3 = ws3.Cells(Rows.Count, "A").End(xlUp).Row

  ' 最終列取得用変数
  Dim maxcol1 As Long, maxcol2 As Long, maxcol3 As Long

  ' 各シートの最終列取得して変数に格納
'  maxcol1 = ws1.Cells(1, Columns.Count).End(xlToLeft).Column
  maxcol1 = 3
  maxcol2 = ws2.Cells(1, Columns.Count).End(xlToLeft).Column
  maxcol3 = ws3.Cells(1, Columns.Count).End(xlToLeft).Column

' ----------

  ' シートの指定したセル範囲を配列として格納する動的配列
  Dim data1() As Variant, data2() As Variant, data3() As Variant

  ' シートの指定した範囲内セル(キーワード列)を配列として動的配列に格納
  data1 = ws1.Range(ws1.Cells(2, 2), ws1.Cells(maxrow1, maxcol1)).Value ' 文字列置換前置換後シート最終行・最終列までを Range で範囲指定、配列として動的配列にセット
  data2 = ws2.Range(ws2.Cells(2, 1), ws2.Cells(maxrow2, maxcol2)).Value ' 都道府県文字列置換リストシート最終行・最終列までを Range で範囲指定、配列として動的配列にセット
  data3 = ws3.Range(ws3.Cells(2, 1), ws3.Cells(maxrow3, maxcol3)).Value ' 県庁所在地文字列置換リストシート最終行・最終列までを Range で範囲指定、配列として動的配列にセット

  ' Range オブジェクトを受け取り、2次元配列で返す自作関数 GetArrFromRange
'  data1 = GetArrFromRange.GetArrFromRange(ws1.Range(ws1.Cells(2, 2), ws1.Cells(maxrow1, maxcol1)))
'  data2 = GetArrFromRange.GetArrFromRange(ws2.Range(ws2.Cells(2, 1), ws2.Cells(maxrow2, maxcol2)))
'  data3 = GetArrFromRange.GetArrFromRange(ws3.Range(ws3.Cells(2, 1), ws3.Cells(maxrow3, maxcol3)))

' ----------

  ' LBound・UBound 関数格納用変数と代入(メモリリーク対策)
  Dim LB_data1_1D As Variant, UB_data1_1D As Variant
  Dim LB_data2_1D As Variant, UB_data2_1D As Variant
  Dim LB_data3_1D As Variant, UB_data3_1D As Variant
  LB_data1_1D = LBound(data1, 1)
  UB_data1_1D = UBound(data1, 1)
  LB_data2_1D = LBound(data2, 1)
  UB_data2_1D = UBound(data2, 1)
  LB_data3_1D = LBound(data3, 1)
  UB_data3_1D = UBound(data3, 1)

  ' Instr 関数で検索文字列を検索、Replace 関数でセル内にある検索文字列を置換文字列に置換(都道府県文字列置換リスト)
  For i = LB_data1_1D To UB_data1_1D ' 配列 data1 の 1次元最大要素までループ処理
    For k = LB_data2_1D To UB_data2_1D ' 配列 data2 の 1次元最大要素までループ処理
'      If InStr(data1(i, 1), data2(k, 1)) > 0 Then ' Instr 関数で配列 data1 の i 行 1列目セル内文字列の中から、配列 data2 の k 行 1列目で指定した文字列を検索して位置を返す
        ' If 文で InStr 関数で文字列を検索して位置を返した場合(> 0)
        data1(i, 1) = Replace(data1(i, 1), data2(k, 1), data2(k, 2)) ' 配列 data1 の i 行 1列目セル内文字列から配列 data2 の k 行 1列目の文字列を検索、配列 data2 の k 行 2列目の文字列に置換して配列 data1 の i 行 1列目に代入
'      End If
    Next k
  Next i

  ' Instr 関数で検索文字列を検索、Replace 関数でセル内にある検索文字列を置換文字列に置換(県庁所在地文字列置換リスト)
  For i = LB_data1_1D To UB_data1_1D ' 配列 data1 の 1次元最大要素までループ処理
    For k = LB_data3_1D To UB_data3_1D ' 配列 data3 の 1次元最大要素までループ処理
'      If InStr(data1(i, 2), data3(k, 1)) > 0 Then ' Instr 関数で配列 data1 の i 行 2列目セル内文字列の中から、配列 data3 の k 行 1列目で指定した文字列を検索して位置を返す
        ' If 文で InStr 関数で文字列を検索して位置を返した場合(> 0)
        data1(i, 2) = Replace(data1(i, 2), data3(k, 1), data3(k, 2)) ' 配列 data1 の i 行 2列目セル内文字列から配列 data3 の k 行 1列目の文字列を検索、配列 data3 の k 行 2列目の文字列に置換して配列 data1 の i 行 2列目に代入
'      End If
    Next k
  Next i

' ----------

  ' LBound・UBound 関数格納用変数と代入(メモリリーク対策)
  Dim UB_data1_2D As Variant
  UB_data1_2D = UBound(data1, 2)

  ' 二次元配列 data2 の内容を、Range で指定したセルから Resize で範囲を変更してセルに代入
  ws1.Range("D2").Resize(UB_data1_1D, UB_data1_2D).Value = data1
  ws1.Activate

  ' テスト用シートに出力する場合は以下のテスト用シートを格納したオブジェクト変数に変更
'  ws4.Range("D2").Resize(UB_data1_1D, UB_data1_2D).Value = data1
'  ws4.Activate

' ----------

  ' 実行速度計測結果表示
  Debug.Print Format(Timer - starttime, "0.00秒")

End Sub