【テクニカル・上級編】Word VBAで『Rangeの比較』を行う:IsEqualメソッドとStart/Endの論理演算 – Word VBA解析バイブル

スポンサーリンク

Word VBAを掌握する極限の知見:`Range`の比較と空間位相解析——`IsEqual`と`Start`/`End`論理演算の真髄

Word VBAにおける最大の罠、それは「文書構造を視覚的な文字列操作の延長線上で捉えること」だ。
画面上のテキストカーソルや選択範囲(Selection)の挙動に惑わされているうちは、中規模以上のドキュメント自動化や複雑なXMLマッピング、あるいは動的な校正システムにおいて、必ず破綻をきたす。

真のシニアアーキテクトは、Word文書を「連続した一次元の文字配列」ではなく、「オーバーラップと包含関係が許容される非ユークリッド的な空間(Text Stream)」として認識する。

今回は、二つの`Range`オブジェクトが文書空間上の同一箇所を指しているか、あるいはどのような包含関係にあるかを極限のパフォーマンスで判定するための技術——`IsEqual`メソッドの厳密な挙動と、`Start`・`End`プロパティによる論理演算の極意を解説する。

1. Wordオブジェクトモデルの根底:なぜ文字列比較では不十分なのか?

初学者が陥る最大のアンチパターンは、二つの`Range`の同一性を評価するために `.Text` プロパティを比較することだ。

‘ 【絶対に行ってはならないアンチパターン】
If range1.Text = range2.Text Then
‘ 同一範囲とみなす(極めて危険)
End If

文書内の異なる場所にある「第1章」という文字列と、別の場所にある「第1章」という文字列は、テキストとしては一致するが、文書空間上の位置(Position)は全く異なる。さらに、フォントの差異、フィールドコードの有無、インラインシェイプの含有などを無視した `.Text` 比較は、システム連携時における致命的なデータ破損を引き起こす。

我々が比較すべきは、文字列ではなく、「文書の原点(Storyの先頭)からのオフセット文字数」である。

2. `Range.IsEqual` メソッドの正確な挙動と限界

Word VBAには、二つのRangeが完全に一致するかを判定するためのネイティブメソッド `IsEqual` が用意されている。

Dim rngA As Range, rngB As Range
‘ (初期化処理は省略)

If rngA.IsEqual(rngB) Then
Debug.Print “完全一致”
End If

チーフアーキテクトの知見:`IsEqual` の内部仕様

`IsEqual` は、単に `Start` と `End` の数値が一致しているかを見ているわけではない。厳密には以下の条件をすべて満たす場合に `True` を返す。
1. 所属するストーリー(`StoryType`:本文、ヘッダー、フッター、脚注など)が同一であること。
2. `Start` プロパティの文字位置が完全に一致していること。
3. `End` プロパティの文字位置が完全に一致していること。

注意点(メモリとパフォーマンス):
`IsEqual` は高速に動作するが、異なるStory間で呼び出すと実行時エラー(エラー 4198: コマンドが失敗しました)を引き起こす場合がある。異なるStory間の比較を行う前には、必ず `rngA.StoryType = rngB.StoryType` のガード節を設けるのが、堅牢なアーキテクチャの鉄則である。

3. `Start` と `End` による空間位相解析(包含・交差判定)

「二つの範囲が重なり合っているか」「一方がもう一方を完全に包含しているか」を判定するには、`Start` と `End` の数値を用いた数学的論理演算(Interval Arithmetic)を駆使する必要がある。

Wordの `Range.Start` および `End` は、文字と文字の間の「隙間(インデックス)」を表す。
例えば、長さ3文字の文字列であれば、Start=0, End=3 となる。

包含関係(Containment)の判定アルゴリズム

`rngParent` が `rngChild` を完全に包含しているかを判定する条件式は以下の通りだ。

‘ rngParent が rngChild を完全に包含しているか?
If rngParent.Start <= rngChild.Start And rngParent.End >= rngChild.End Then
‘ 包含関係成立
End If

交差(Intersection)と重複削除の極意

数万行に及ぶ法律文書や規格書から、特定のタグに挟まれた領域を抽出し、重複やネストを整理するシステムを構築する場合、交差判定が必須となる。

以下のコードは、配列やコレクションに格納された無秩序な `Range` 群から、重複や完全包含を排除し、文書の先頭から順にユニークな処理を行うためのアーキテクチャの雛形である。

‘ ==============================================================================
‘ 漆黒のアーキテクチャ:Range群の空間的重複排除と統合ロジック
‘ ==============================================================================
Sub OptimizeAndProcessRanges(ByRef targetDoc As Document)
Dim colRanges As New Collection
Dim i As Long, j As Long

‘ (前提: colRanges に動的に抽出した Range オブジェクトが格納されているとする)

‘ バブルソートまたはクイックソートにより、Start位置で昇順ソートを実施
‘ ※実運用では配列に移してO(N log N)でソートすることを推奨

‘ 統合・重複排除のメインループ
For i = colRanges.Count To 2 Step -1
Dim currentRng As Range
Dim prevRng As Range
Set currentRng = colRanges(i)
Set prevRng = colRanges(i – 1)

‘ 同一ストーリーの確認
If currentRng.StoryType = prevRng.StoryType Then

‘ 1. 完全一致または前方による包含の排除
If prevRng.Start <= currentRng.Start And prevRng.End >= currentRng.End Then
‘ prevRng が currentRng を完全に包含している場合、currentRngを破棄
colRanges.Remove i

‘ 2. 範囲の一部が重複している場合(インターセクト)の合流処理
ElseIf prevRng.End >= currentRng.Start And prevRng.End < currentRng.End Then ' 範囲を結合して拡張する prevRng.End = currentRng.End colRanges.Remove i End If End If ' オブジェクト参照の明示的破棄(メモリリーク防止) Set currentRng = Nothing Set prevRng = Nothing End For ' クリーンナップされた Range 群に対する一括処理 For i = 1 To colRanges.Count ExecuteBusinessLogic colRanges(i) Set colRanges(i) = Nothing 0 Next i End Sub Private Sub ExecuteBusinessLogic(ByRef rng As Range) ' 実際の高度な文書処理(スタイル適用、XML付与など) ' 例: rng.Font.Color = wdColorRed End Sub ---

4. 極限環境下におけるパフォーマンス最適化とメモリマネジメント

Word VBAの開発において、COMオブジェクトの解放を怠ることは、ガベージコレクションの未熟なVBAランタイムに致命的なメモリリーク(HRESULT 8007000E: メモリ不足)をもたらす。

特に `Range` オブジェクトは、文書の編集(文字の挿入・削除)を行うたびに、内部のポインタが再計算される。ループ内で無秩序に `Range` を生成・破棄すると、Wordのプロセス(WINWORD.EXE)のメモリ使用量は肥大化し、最終的にクラッシュする。

シニアエンジニアが守るべき3大鉄則

1. ループ内のRange再生成を避ける
既存の `Range` 変数に対して `.SetRange` メソッドを使用し、インスタンスの生成コスト(Marshalling Cost)を極限まで抑制せよ。

Dim rngWorker As Range
Set rngWorker = activeDocument.Content

Dim p As Long
For p = 10 To 1000 Step 50
‘ New を使わず、既存インスタンスの範囲を再設定する
rngWorker.SetRange Start:=p, End:=p + 10
‘ 処理…
Next p
Set rngWorker = Nothing

2. オブジェクト変数は明示的に `Nothing` を代入する
VBAのスコープを抜ける際の暗黙の解放に頼るな。特にコレクションやループ内で生成した `Range` は、使い終わった瞬間に `Set xxx = Nothing` を実行し、参照カウントを即座にデクリメントさせよ。

3. 画面描画とイベントの完全遮断
空間位相解析や大量のRange比較を行うアルゴリズムを実行する際は、必ず以下をコードの冒頭と末尾に配置せよ。

Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = wdCalculationManual

‘ — 圧倒的な速度でRange演算を実行 —

Application.Calculation = wdCalculationAutomatic
Application.EnableEvents = True
Application.ScreenUpdating = True

総括

Word VBAにおける `Range` の比較とは、単なる値の突合ではない。それはWordというドキュメントエンジンの内部構造(Dnode / Piece Table)を理解し、その空間座標を自在に支配するための高度な幾何学的演算である。

`IsEqual` による厳密な同一性判定と、`Start` / `End` を駆使した論理演算、そして厳格なメモリ管理。この三位一体を習得したとき、あなたの書くVBAコードは、単なる「マクロ」の領域を脱し、堅牢でエンタープライズな「文書解析エンジン」へと昇華するだろう。

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