【実務・中級編】【初心者向け】検索結果のRangeを「選択」せずに直接操作して処理を高速化する基本テクニック – Word VBA解析バイブル

スポンサーリンク

Word VBAにおいて、文字の検索や置換は最も頻度の高い処理の一つだ。しかし、ネット上で散見されるサンプルコードの多くは、初心者向けと称して致命的なアンチパターンを教えている。

それが、`Selection.Find` や `Select` メソッドの乱用だ。

画面がパチパチと切り替わり、処理が完了するまでやたらと時間がかかるマクロを見たことはないだろうか。あれはWordに「目に見える形でカーソルを動かせ」と強制しているからに他ならない。

今回は、プロの現場で生き残るための鉄則として、「選択(Select)せずに、Rangeオブジェクトで裏側を高速に支配する」ための実践的知見を授けよう。

—

なぜ `Select` を使うとマクロは遅くなるのか?

WordのUI(ユーザーインターフェイス)と、内部のデータ構造(DOMのようなもの)は完全に分離されている。

  • `Selection` オブジェクト: ユーザーが画面上で選択している「目に見える」領域。Wordはこれを描画するために莫大なリソースを消費する。
  • `Range` オブジェクト: 文書内の「目に見えない」抽象的な位置・領域。描画の必要がないため、メモリ上で超高速に処理できる。

`Selection.Find` を使うと、Wordはわざわざ画面上のカーソルを移動させ、ハイライトを引き、スクリーンの再描画(ScreenUpdating)を行う。これがパフォーマンスを殺す最大の原因だ。

さらに悪いことに、`Selection` はカーソルの位置やユーザーの操作に強く依存するため、「意図しない場所を置換してしまった」「エラーで止まった時にどこを選択しているか分からない」といったバグの温床になる。

実務で使うマクロに `Select` は一切不要だ。すべてを `Range` で完結させなければならない。

—

堅牢な検索置換の基本設計:`Range.Find` の正しい作法

Rangeオブジェクトを使った検索・置換の基本構造は以下の通りだ。
ここで最も重要なのは、「検索条件をリセットする(ClearFormatting)」ことと、「ループの終了条件を正しく記述する」ことである。

Sub CleanSearchAndReplace_Basic()
Dim rngTarget As Range
Set rngTarget = ActiveDocument.Content ‘ 文書全体をRangeとして取得

‘ 検索条件の設定と実行
With rngTarget.Find
.ClearFormatting
.Replacement.ClearFormatting

.Text = “旧キーワード”
.Replacement.Text = “新キーワード”
.Forward = True
.Wrap = wdFindStop ‘ 文書の末尾で検索を停止する(重要)
.Format = False
.MatchCase = True
.MatchWholeWord = True

‘ 一括置換を実行(返り値で成功判定が可能)
.Execute Replace:=wdReplaceAll
End With

Set rngTarget = Nothing
End Sub

プロの現場で必ず守るべき設計ポイント

1. `wdFindStop` の指定: `Wrap` プロパティには必ず `wdFindStop` を指定すること。`wdFindContinue` にすると、文書の末尾から先頭に戻って無限ループに陥るリスクが生じる。
2. 条件のクリア: `.ClearFormatting` を入れないと、前回Wordの検索窓に残った書式設定(太字やフォントなど)が引き継がれ、突然ヒットしなくなるバグを生む。

—

【実践】見えない領域で特定の文字列を「装飾付き」に置換する

単なるテキスト置換であれば上記の `Execute Replace:=wdReplaceAll` で十分だが、実務では「ヒットした箇所を太字にする」「特定の文字列だけ背景色を変える」といった、条件付きの動的処理が求められる。

ここでも `Select` は使わない。`Execute` メソッドが `True` を返す間、`Range` 自体がヒットした文字列の位置に移動(シフト)するという特性を利用する。

以下のプロダクションコードは、文書内の特定の社名を検出し、それを太字にしつつ文字色を青に変える実用的なモジュールだ。

Sub AdvancedRangeProcess()
Dim rngDoc As Range
Set rngDoc = ActiveDocument.Content

‘ 画面描画と警告を停止してパフォーマンスを極限まで引き上げる
Application.ScreenUpdating = False
Application.DisplayAlerts = wdAlertsNone

On Error GoTo ErrorHandler

With rngDoc.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = “株式会社サンプル”
.Forward = True
.Wrap = wdFindStop
.MatchCase = True

‘ ループを回してヒットしたRangeを個別に直接操作する
Do While .Execute
‘ rngDoc自体がヒットした文字列のRangeに書き換わっている
With rngDoc
.Bold = True
.Font.Color = wdColorBlue

‘ 【超重要】
‘ 処理が終わったら、Rangeの末尾にcollapse(折り畳み)し、
‘ 「次回の検索がこのヒット箇所の内部から始まって無限ループする」のを防ぐ
.Collapse wdCollapseEnd
End With
Loop
End With

ErrorHandler:
‘ 確実な後始末(エラー有無に関わらず描画を戻す)
Application.ScreenUpdating = True
Application.DisplayAlerts = wdAlertsAll

If Err.Number <> 0 Then
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical
Else
MsgBox “処理が高速に完了しました。”, vbInformation
End If

Set rngDoc = Nothing
End Sub

このコードのアーキテクチャ上の優位性

  • 完全なバックグラウンド処理: `ScreenUpdating = False` と `Range` 操作の組み合わせにより、何百ページある巨大な仕様書であっても、人間が認識できないほどのスピードで一瞬で処理が終わる。
  • 無限ループの防止 (`Collapse`): `Range.Find` のループ処理において最も多いバグが、同じ場所を延々と探し続ける無限ループだ。処理を行った直後に `rngDoc.Collapse wdCollapseEnd` を実行することで、検索開始位置を「現在のヒット箇所の直後」に強制移動させている。この一手間がコードの堅牢性を担保する。

—

データベース・外部ファイル連携時の注意点

こうした高速なRange操作マクロを、Excelや外部データベース(SQL Server等)から読み込んだ置換リストと連携させる場合、さらに気をつけるべき点がある。

それは、「置換文字列の中にワイルドカード文字(“や`?`など)が含まれている場合の安全性」だ。

WordのFind機能はデフォルトでワイルドカードを解釈するため、意図しない文字列まで巻き込んで置換されてしまう事故が起きる。外部データを流し込む場合は、必ず `.MatchWildcards = False` を明示的に設定し、完全一致(あるいはプレーンテキストとしての検索)を担保すること。

また、数千件に及ぶ置換リストを配列(Array)でメモリ上に保持し、ループ内で `Range.Find` を回すようなアーキテクチャにする場合、文書オブジェクトへのアクセス回数を最小限に抑えることが、システム全体の寿命を延ばす鍵となる。

結びにかえて

`Selection` から脱却し、`Range` を手なずけること。それは単にマクロの実行スピードが速くなるというだけではない。

「画面のちらつきに怯える必要のない、バグの起きない堅牢なコード」を書けるようになったという、エンジニアとしての確かなステップアップを意味する。

明日からの開発では、コードのどこかに `Selection` が隠れていないか、厳しく目を光らせてほしい。あなたの書くVBAは、もっと速く、もっと美しくなるはずだ。

タイトルとURLをコピーしました