【実務・中級編】【初心者向け】Word表の全セルを走査し、特定のキーワードが含まれるセルのみ背景色を塗る – Word VBA解析バイブル

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

【Word VBA】表操作の罠を回避せよ:セル走査と条件付き書式の「プロフェッショナルな実装」

「特定の単語が含まれるセルに色を塗る」

一見すると、プログラミング初学者が最初にぶつかる、極めて単純なタスクに見えるだろう。しかし、実務における「業務自動化ツール」という文脈において、この単純な要件こそが、開発者の設計思想(アーキテクチャ)の良し悪しを露骨に映し出す鏡となる。

多くの初心者は、教科書通りの「行と列を二重ループで回す」コードを書き、それが動いたことに満足する。だが、そのコードを複雑な構造を持つ実務文書、あるいは数千行に及ぶ巨大な表に投入した瞬間、プログラムは悲鳴を上げ、あるいは沈黙(フリーズ)する。

なぜ、あなたの書いたコードは現場で通用しないのか?
今日は、Word VBAにおける「表(Table)操作」の深淵について、単なる実装手法を超えた「堅牢な設計」の観点から伝授しよう。

1. 初心者が陥る「二重ループの罠」

まず、典型的な「動くが、危うい」コードを見てみよう。

' ⚠️ 避けるべき実装例:脆弱な設計
Sub NaiveCellColoring()
    Dim tbl As Table
    Dim r As Integer, c As Integer
    Dim targetWord As String
    
    targetWord = "重要"
    Set tbl = ActiveDocument.Tables(1)
    
    ' 行と列のインデックスによる二重ループ
    For r = 1 To tbl.Rows.Count
        For c = 1 To tbl.Columns.Count
            ' ❌ ここでエラーが発生するリスクが極めて高い
            If InStr(tbl.Cell(r, c).Range.Text, targetWord) > 0 Then
                tbl.Cell(r, c).Shading.BackgroundPatternColor = wdColorYellow
            End If
        Next c
    Next r
End Sub

なぜこのコードは「設計ミス」なのか?

このコードには、プロの現場では決して許容されない致命的な欠陥が2つある。

1. 結合セルの崩壊(Merged Cells Issue):
Wordの表において「セルの結合」は、インデックスベースのアクセス(`Cell(r, c)`)にとって最大の敵だ。結合されたセルが存在する場合、`Columns.Count` や `Rows.Count` は期待通りの値を返さず、存在しないセルにアクセスしようとした瞬間に `実行時エラー ‘5941’: 指定したセルが見つかりません` を吐いて停止する。
2. パフォーマンスの欠如:
`tbl.Cell(r, c)` を呼び出すたびに、Wordのオブジェクトモデルは内部的に複雑な計算を行い、セルを特定しようとする。巨大な表に対してこの計算を繰り返すのは、エンジニアリングとして極めて非効率だ。

2. プロフェッショナルが採用する設計思想

堅牢なツールを作るためには、以下の3つの原則を守る必要がある。

原則Ⅰ:オブジェクトの走査は「コレクション」で行う

インデックス(行番号・列番号)に依存するのではなく、`Table.Range.Cells` というコレクションを直接イテレート(反復)せよ。これにより、結合セルの有無に左右されず、存在するすべてのセルを確実に、かつ安全に走査できる。

原則Ⅱ:防御的プログラミング(Defensive Programming)

「対象が表であること」「対象が空でないこと」を事前に検証する。ユーザーの操作ミスや、想定外の文書構造に対して、エラーで落ちるのではなく、適切に処理を中断・通知するのがプロの仕事だ。

原則Ⅲ:責務の分離(Separation of Concerns)

「検索ロジック」と「書式設定ロジック」を分離せよ。将来的に「色を塗る」のではなく「枠線を太くする」という仕様変更があっても、検索ロジックを書き直す必要がない設計にするのだ。

3. プロダクション・グレードの実装例

以上の設計思想をすべて盛り込んだ、そのまま実務の基盤として使えるコードを提示する。

Option Explicit

''' <summary>
''' 表内の全セルを走査し、特定のキーワードを含むセルの背景色を変更する。
''' </summary>
''' <param name="targetTable">対象となるWord Tableオブジェクト</param>
''' <param name="keyword">検索する文字列</param>
''' <param name="highlightColor">適用する背景色</param>
Sub HighlightCellsByKeyword( _
    ByRef targetTable As Table, _
    ByVal keyword As String, _
    ByVal highlightColor As Long)

    ' 1. 引数のバリデーション(防御的プログラミング)
    If targetTable Is Nothing Then
        MsgBox "エラー: 対象の表が指定されていません。", vbCritical
        Exit Sub
    End If
    
    If keyword = "" Then
        MsgBox "エラー: 検索キーワードが空です。", vbExclamation
        Exit Sub
    End If

    ' エラーハンドリングの開始
    On Error GoTo ErrorHandler
    
    ' 2. セルコレクションの走査(結合セルに強い実装)
    Dim targetCell As Cell
    Dim cellText As String
    
    ' 描画更新を停止してパフォーマンスを向上させる(Wordでは直接制御が難しいため、
    ' 処理の粒度を意識することが重要)
    
    For Each targetCell In targetTable.Range.Cells
        ' セル内のテキストを取得(Wordのセル末尾には特殊文字が含まれるため注意)
        cellText = targetCell.Range.Text
        
        ' セル末尾の制御文字(Chr(13) & Chr(7))を除去して比較精度を高める
        cellText = CleanCellText(cellText)
        
        ' 3. 条件判定と実行(InStr関数による部分一致)
        If InStr(1, cellText, keyword, vbTextCompare) > 0 Then
            targetCell.Shading.BackgroundPatternColor = highlightColor
        End If
    Next targetCell

    Exit Sub

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

''' <summary>
''' セル内のテキストからWord特有の制御文字を除去するヘルパー関数
''' </summary>
Private Function CleanCellText(ByVal txt As String) As String
    ' セル末尾のEnd-of-cell marker (Chr(13) & Chr(7)) を除去
    CleanCellText = Replace(Replace(txt, Chr(13), ""), Chr(7), "")
End Function

''' <summary>
''' 実行用エントリーポイント
''' </summary>
Sub Main()
    Dim tbl As Table
    Dim targetWord As String
    
    ' 実行環境のチェック
    If Selection.Information(wdWithInTable) = False Then
        MsgBox "表の中にカーソルを置いて実行してください。", vbExclamation
        Exit Sub
    End If
    
    Set tbl = Selection.Tables(1)
    targetWord = "重要" ' ここを外部ファイルやダイアログから取得するように拡張可能
    
    ' 処理の開始
    Debug.Print "処理開始: " & Now
    HighlightCellsByKeyword tbl, targetWord, wdColorYellow
    Debug.Print "処理完了: " & Now
End Sub

4. 業務自動化エンジニアへのアドバイス

このコードをマスターした君に、さらなる高みへ進むための視点を与えておこう。

外部データ連携における「汚染」への警戒

もしこのキーワードをExcelやデータベースから取得してくる場合、「目に見えない文字」に細心の注意を払え。Excelのセルに含まれる改行コードや、DBから取得した際の半角スペースが原因で、`InStr` が一致しないというトラブルは日常茶飯事だ。`Trim` 関数や `Replace` 関数を使い、データを「クレンジング」してから比較に回す癖をつけろ。

スケーラビリティの考慮

表が数千行に及ぶ場合、`For Each` で回しても処理時間はかかる。もし極限の速度を求めるなら、Wordの `Find` オブジェクトを使用して、テキストを直接検索し、その `Range`(範囲)から親の `Cell` を特定するというアプローチがある。しかし、それは「コードの可読性」と「保守性」を犠牲にするトレードオフだ。まずは、今回提示した「安全で読みやすいコード」をベースに、現場の要求レベルを見極めることだ。

結論

「動く」ことと、「運用に耐えうる」ことは全く別物だ。
結合セルの存在を予見し、エラーを未然に防ぎ、役割を分担したコードを書く。この一歩が、君を単なる「マクロ使い」から、信頼される「自動化エンジニア」へと変貌させる。

設計に妥協するな。コードに魂を込めろ。

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