【実務・中級編】【初心者向け】表のセル内にある特定キーワードを検出し、その行全体を強調表示するマクロ – Word VBA解析バイブル

スポンサーリンク
スポンサーリンク

「動けばいい」は素人の思考だ。Word VBAにおける表操作を、プロフェッショナルの設計へと昇華させる

業務自動化の現場において、「Wordの表から特定の単語を探して、その行の色を変える」というタスクは、一見すると初歩的な課題に見える。しかし、現場で「使い物にならない(壊れる、遅い、バグが出る)」コードを量産しているエンジニアは少なくない。

もし君が、`Selection`オブジェクトを多用し、セル内の文字列比較で「なぜか一致しない」という不可解な挙動に頭を抱えているとしたら、君はまだ「オブジェクトの真の性質」を理解していない。

本稿では、単なるコピペ用コードの提供はしない。「なぜその書き方が致命的なのか」、そして「実務で耐えうる堅牢な設計とは何か」を、アーキテクトの視点から徹底的に解説する。

1. 初心者が陥る「3つの致命的な罠」

設計に入る前に、多くの開発者が犯す典型的なミスを整理しておく。これらを避けることが、プロへの第一歩だ。

罠①:`Selection` オブジェクトへの依存

多くの教材では `Selection.Range` を使ったコードが紹介されている。しかし、`Selection` は「ユーザーがいま画面上でどこを選択しているか」という、極めて不安定なUI状態に依存する。
マクロ実行中にユーザーがマウスを動かせば、処理対象は容易に狂う。自動化において、`Selection` や `Activate` は「設計の放棄」と同義である。

罠②:セル末尾の「不可視文字」を見逃す

Wordのセル内のテキストを取得すると、末尾に必ず `Chr(13) & Chr(7)`(キャリッジリターンとベル文字)という特殊な制御文字が付与される。
`If cell.Range.Text = “完了” Then` と書いても、実際には `”完了” & Chr(13) & Chr(7)` と比較しているため、論理的に絶対に一致しない。 これが「コードは正しいはずなのに動かない」最大の原因だ。

罠③:計算量の無視(O(n^2) の恐怖)

表の行数、列数が増大した際、ループの中で毎回オブジェクトのプロパティを不必要に参照し続けると、パフォーマンスは指数関数的に悪化する。特に、書式設定(`Shading` や `Font`)の変更はコストが高い。

2. プロフェッショナルな設計思想:関数の分離

堅牢なコードを書くためには、「探索ロジック」と「描画(書式)ロジック」を分離すべきである。

1. 検索エンジン(Logic): 表をスキャンし、条件に合致する行のインデックスを特定する。
2. スタイラー(View): 指定された行に対して、あらかじめ定義されたスタイルを適用する。

このように責務を分けることで、将来的に「背景色を変えるのではなく、フォントを赤くしたい」といった仕様変更があっても、検索ロジックを一切汚さずに対応が可能になる。

3. 【プロダクション・コード】堅牢な行強調マクロ

以下に、実務に投入可能なレベルのコードを示す。単なる書き写しではなく、コメントに記した「意図」を読み解いてほしい。

Option Explicit

''' <summary>
''' 表内の特定キーワードを検出し、該当する行全体にハイライトを適用するメインルーチン
''' </summary>
Public Sub HighlightRowsByKeyword()
    ' --- 設定領域(定数化することで保守性を高める) ---
    Const TARGET_KEYWORD As String = "重要"
    Const HIGHLIGHT_COLOR As Long = 65535 ' 黄色 (vbYellow)
    Const FONT_COLOR As Long = 255       ' 赤 (vbRed)
    
    Dim targetDoc As Document
    Dim targetTable As Table
    Dim i As Long, j As Long
    Dim isMatch As Boolean
    
    ' オブジェクトの取得(Selectionに依存しない)
    Set targetDoc = ActiveDocument
    
    ' 文書内に表が存在するかチェック(ガード節)
    If targetDoc.Tables.Count = 0 Then
        MsgBox "処理対象の表が見つかりません。", vbExclamation
        Exit Sub
    End If
    
    ' 本来は引数などで表を指定すべきだが、ここでは最初の表を対象とする
    Set targetTable = targetDoc.Tables(1)
    
    ' 高速化のための最適化(WordにはExcelのScreenUpdatingのような設定はないが、
    ' 描画更新を最小限にするため、ループ内でSelectを行わないことが重要)
    
    On Error GoTo ErrorHandler
    
    ' --- 探索フェーズ ---
    For i = 1 To targetTable.Rows.Count
        isMatch = False
        
        For j = 1 To targetTable.Columns.Count
            ' セル内のテキストを取得し、制御文字を除去して比較
            If CheckCellText(targetTable.Cell(i, j).Range.Text, TARGET_KEYWORD) Then
                isMatch = True
                Exit For ' 1つでもヒットすればその行の探索は終了
            End If
        Next j
        
        ' --- 描画フェーズ ---
        If isMatch Then
            ApplyRowStyle targetTable.Rows(i), HIGHLIGHT_COLOR, FONT_COLOR
        End If
    Next i
    
    MsgBox "処理が完了しました。", vbInformation
    Exit Sub

ErrorHandler:
    MsgBox "予期せぬエラーが発生しました: " & Err.Description, vbCritical
End Sub

''' <summary>
''' セルのテキストが特定のキーワードを含んでいるか判定する
''' </summary>
''' <param name="cellText">セルから取得したRawテキスト</param>
''' <param name="keyword">検索キーワード</param>
''' <returns>一致すればTrue</returns>
Private Function CheckCellText(ByVal cellText As String, ByVal keyword As String) As Boolean
    ' Wordのセル末尾に含まれる特殊文字 (Chr(13) & Chr(7)) を除去
    ' これを行わないと、文字列比較が正しく動作しない
    Dim cleanText As String
    cleanText = Replace(cellText, Chr(13) & Chr(7), "")
    
    ' 部分一致判定(InStrを使用)
    If InStr(1, cleanText, keyword, vbTextCompare) > 0 Then
        CheckCellText = True
    Else
        CheckCellText = False
    End If
End Function

''' <summary>
''' 指定された行に対して書式を一括適用する
''' </summary>
Private Sub ApplyRowStyle(ByRef targetRow As Row, bgColor As Long, fontColor As Long)
    ' Withブロックを使用してオブジェクトへのアクセス回数を減らし、パフォーマンスを向上させる
    With targetRow.Range
        .Shading.BackgroundPatternColor = bgColor
        .Font.Color = fontColor
        .Font.Bold = True
    End With
End Sub

4. アーキテクトによる技術解説

なぜ `Replace` を使っているのか?

`CheckCellText` 関数内で `Replace(cellText, Chr(13) & Chr(7), “”)` を実行している。これは、Word特有の「セルの終わりを示すマーカー」を物理的に削ぎ落とす作業だ。これを行うことで、初めて `InStr` による純粋な文字列比較が可能になる。

なぜ `Exit For` が重要なのか?

行の中に複数のキーワードが含まれている場合、最初の1つを見つけた時点でその行の「強調」という目的は達成されている。無駄に同じ行の他のセルをループし続けるのは、リソースの浪費だ。`Exit For` は、計算量を削減するための極めて重要な「早期脱出」である。

外部連携(DB/ファイル)への拡張性

もし、このキーワードがExcelやSQLデータベースから渡される場合、コードの構造はさらに進化させる必要がある。

  • Bad Practice: ループの中で毎回SQLを発行する(N+1問題)。
  • Best Practice: 最初に全てのキーワードを `Dictionary` オブジェクトに読み込み、メモリ上で高速に照合する。

5. まとめ:プロフェッショナルの心得

優れた自動化ツールとは、単に「動く」ものではない。
「想定外のデータが来ても壊れない」「後から誰が見ても意図が伝わる」「変化に対して柔軟である」。この3点を満たして初めて、プロダクション・コードと呼べる。

今回紹介した「制御文字の除去」「責務の分離」「ガード節によるエラー回避」は、Word VBAに限らず、あらゆるプログラミングに通ずる鉄則である。

次に君がコードを書くとき、自分自身に問いかけてほしい。
「そのコードは、`Selection` という不確実性に頼っていないか?」と。

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