LoginSignup
8

More than 3 years have passed since last update.

【VBA】WORD文書の範囲を指定して操作する (Rangeオブジェクト)

Last updated at Posted at 2020-09-24

はじめに

WORDでVBAを使用する場合は、文書中の操作したい範囲を適切に指定しないと処理を上手く実行することができません。
ここでは、WORD VBAで文書の範囲を定義する「Rangeオブジェクト」について、備忘としてまとめておきます。

<目次>
1. 開始位置と終了位置を指定して範囲を取得する
 1-1. Rangeメソッドを用いた簡素な記述
 1-2. Rangeメソッドを用いた具体例
2. 文字列検索をして範囲を取得する
 2-1. Selectionオブジェクトから文字列を検索する
 2-2. Rangeオブジェクトから文字列を検索する
3. Rangeプロパティを使用して範囲を取得する

●参考サイト:Wordにおける構成要素とRange

1. 開始位置と終了位置を指定して範囲を取得する

Documentオブジェクトが有する「Rangeメソッド」というものを使用することで、開始位置と終了位置を指定した範囲の取得ができます。
具体例を見た方が早いので、サンプルコードを挙げて説明していきます。

1-1. Rangeメソッドを用いた簡素な記述

サンプルコード1-1-1:文書中の2~3文字目を赤でマーキングする例
Sub Range1()
    ActiveDocument.Range(Start:=1, End:=3).HighlightColorIndex = wdRed
End Sub

(実行結果)
2020-09-22 173251.png
この例は文書中の2文字目から3文字目までをマーキングするだけのコードです。
記述しているコードは1行のみですが、これを先頭部分から簡単に説明しておきます。

最初のActiveDocumentの部分は、現在開いているファイル(Documentオブジェクト)を返しています。

続くRange(Start:=1, End:=3)Rangeメソッド)では、文書の領域を開始位置、終了位置を用いて指定しています。これは、簡単にRange(1, 3)と書くこともできます。
2文字目から3文字目の領域を取得する場合は、次の図のように、開始位置1から終了位置3の範囲で指定することになります。
改行コードも1文字分としてカウントされますのでご注意ください。
2020-09-22 175348.png
なお、Range(1)というように終了位置を省略した場合は、開始位置1から文書末尾までの範囲が指定されることになります。
また、このRangeメソッドはDocumentオブジェクトのみで使用できるメソッドであることをご留意ください。

最後のHighlightColorIndexは、Rangeオブジェクトのプロパティであり、これによりマーキングの色を指定しています。
指定方法は.HighlightColorIndex = wdRedという形になります。
ここでは、wdRedで赤色を指定していますが、その他の色については「WdColorIndex 列挙 (Word)」を参照してください。

サンプルコード1-1-2:文書中の3~5文字目をカタカナに置換する例
Sub Range2()
    Dim rng As Range 'オブジェクト変数の設定
    Set rng = ActiveDocument.Range(2, 5)
    rng.Text = "ウエオ"
    Set rng = Nothing 'オブジェクトの解放
End Sub

(実行結果)
2020-09-23 114757.png
これは指定範囲の文字列を置換するコードです。
オブジェクト変数を使用した書き方にしているためコードは4行になっていますが、最初のマーキングの例と同様に1行(ActiveDocument.Range(2, 5).Text = "ウエオ")で書くことも可能です。

サンプルコードについて簡単に説明します。
まず、1行目と2行目で、オブジェクト変数rngに領域を指定するオブジェクトActiveDocument.Range(2, 5)を代入しています。
厳密にはオブジェクトそのものを代入しているわけではないのですが、ここでは触れません(詳しくは、オブジェクト変数とSetステートメントという記事を参照してください。)。
ここでは、3文字目から5文字目の領域を取得するので、Rangeオブジェクトは開始位置2から終了位置5の範囲で指定しています。

次の、3行目のrng.Text = "ウエオ"では、RangeオブジェクトのTextプロパティを用いて文字列を変換しています。
なお、ここで、次の行にrng.Bold = Trueと追加すると、変換した文字列を太字にすることもできます(その他にも様々なプロパティがありますので、詳細は「Range オブジェクト」で確認してみてください。)。

最後の4行目Set rng = Nothingは、作成したオブジェクトを解放するための記述です(オブジェクトの解放について詳しく知りたい方は、「Set a = Nothing」のお話という記事を参照してください。)。

1-2. Rangeメソッドを用いた具体例

サンプルコード1-2-1:文書中の数字部分を黄色でマーキングする例
Const numChr = "〇一二三四五六七八九十百千万億兆01234567890123456789" '漢数字のみの判定であれば後半の算用数字は不要

Sub HighlightTest()
    Dim i As Long: i = 0 '指定しなくとも初期値は0となる
    Do While i < ActiveDocument.Content.End
        If InStr(numChr, ActiveDocument.Range(i, i + 1).Text) > 0 Then
            ActiveDocument.Range(i, i + 1).HighlightColorIndex = wdYellow
        End If
        i = i + 1
    Loop
End Sub

(実行結果)
2020-09-23 140431.png
これは、文書を先頭から1文字ずつ判定して、数字である場合に黄色のマーキングを行うコードです。
コードの実行結果を見るために漢数字が必要であることから、変換文書には法律(消費税法)を使用しています。

Sub HighlightTest()(プロシージャ)の2行目に出てくるActiveDocument.Content.Endは、文書の末尾の文字位置を取得するための記述です。
Contentプロパティは、メイン文書全体のRangeオブジェクトを返します。つまり、文書全体を領域として持つRangeオブジェクトが取得されることになります。
そして、Endプロパティは、指定範囲(Rangeオブジェクト)の末尾の文字位置を取得するプロパティです。
これらのプロパティを利用することで、文書の末尾の位置を特定しています。

3行目のActiveDocument.Range(i, i + 1).Textでは、文書の先頭から1文字ずつ文字を取得しています。
この文字が、冒頭に定義したnumChrに含まれているか否かを、InStr関数を用いて判定しています。

サンプルコード1-2-2:【参考】文書中の漢数字を算用数字に変換する例
Const numChr = "〇一二三四五六七八九十百千万億兆01234567890123456789"

Sub ConvNumTest()
    Dim i As Long
    Dim s As Long '数字部分のスタート位置を記録する変数
    Dim rng As Range
    Dim numFlg As Boolean: numFlg = False '数字の場合にTrueとするフラグ

    With ActiveDocument 'ActiveDocumentの記述を省略するステートメント
        Do While i < .Content.End
            If InStr(numChr, .Range(i, i + 1).text) > 0 Then '文字が数字であるか否かの判定
                If numFlg = False Then
                    s = i '数字が出現したスタート位置を記録
                    numFlg = True '数字部分に入ったためTrueに変更
                End If
            Else
                If numFlg = True Then 'numFlgがTrueであれば直前までが数字であったということ
                    Set rng = .Range(s, i) '数字部分のRangeを変数rngに設定
                    rng.text = ConvKan2Num(rng.text) 'ConvKan2Num関数を使って漢数字を算用数字に変換
                    i = s + Len(rng.text) '変換後の数字の末尾にiを再設定(文字数が変わるため)
                    numFlg = False
                End If
            End If
            i = i + 1
        Loop
    End With
End Sub

(以下は、漢数字を算用数字に変換する関数)

'漢数字(二三五四一)を単純変換する関数
Function ReplKan2Num(text As String) As Double
    Dim chinNums As Variant: chinNums = Array("〇", "一", "二", "三", "四", "五", "六", "七", "八", "九")
    Dim arabNums As Variant: arabNums = Array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9")
    Dim i As Long
    For i = 0 To UBound(chinNums)
        text = Replace(text, chinNums(i), arabNums(i))
    Next i
    ReplKan2Num = Val(StrConv(text, vbNarrow)) 'vbNarrowは入力文字列が"23541"という全角数字の場合に対応させるため
End Function

'15桁までの漢数字を数値変換する関数
Function ConvKan2Num(text As String, Optional digit As Long = 4) As Double
    Dim i As Long, pos As Long, posStart As Long: posStart = 1
    Dim partialValue As Double
    Dim largeNums As Variant: largeNums = IIf(digit = 4, Array("", "万", "億", "兆"), Array("", "十", "百", "千")) '大数の指定
    For i = UBound(largeNums) To 0 Step -1
        If i = 0 Then
            pos = IIf(posStart = Len(text) + 1, 0, Len(text) + 1)
        Else
            pos = InStr(text, largeNums(i))
        End If
        If pos <> 0 Then '目的の大数が無い場合は加算しない
            If pos = posStart Then '目的の大数が数字を持たない場合(ex. "二千百四十"における百の位)
                partialValue = 1
            Else
                If digit = 4 Then
                    partialValue = ConvKan2Num(Mid(text, posStart, pos - posStart), 1) '再帰処理
                Else
                    partialValue = ReplKan2Num(Mid(text, posStart, pos - posStart))
                End If
            End If
            ConvKan2Num = ConvKan2Num + partialValue * 10 ^ (digit * (i))
            posStart = pos + 1
        End If
    Next i
End Function

(実行結果)
2020-09-23 151137.png
これは、文書中の漢数字を算用数字に変換するコードです。

前半部分のSub ConvNumTest()(プロシージャ)では、Rangeメソッドを使用して数字部分の範囲を取得した上で、算用数字への変換処理を行っています。

後半部分のReplKan2Num関数及びConvKan2Num関数は、漢数字を算用数字に変換するコードです。この内容は、本記事の趣旨から離れるため説明は省略します。
変換のアルゴリズムについてはこちらの記事に書いてありますので、必要に応じてご参照ください。

2. 文字列検索をして範囲を取得する

2-1. Selectionオブジェクトから文字列を検索する

Selectionオブジェクトは選択範囲を表すオブジェクトです。
このSelectionオブジェクトのFindプロパティ(検索機能)を使って、対象文字列を選択して処理する例を挙げていきます。

サンプルコード2-1-1:文字列を1つだけ検索してマーキングする例(Selection)
Sub FindBySelection1()
    Selection.Find.Execute FindText:="又は"
    Selection.Range.HighlightColorIndex = wdYellow
End Sub

(実行結果)
2020-09-24 012620.png
この例では、カーソル位置(選択位置)以降で最初に検索された文字列のみを選択して、黄色のマーキングをしています。

1行目のSelection.Find.Execute FindText:="又は"では、検索された文字列を選択するまでの処理を行っています。「"又は"」の部分を変えることで、任意の文字列の検索をすることができます。
この1行は、Selection.Find.Execute ("又は")と記述しても同じ処理が行われます。

この1行目を簡単に説明しますと、最初のSelectionは、文字列の選択範囲を表すSelectionオブジェクトです。検索実行後は、選択範囲が検索文字列に移行します。
次のFindは、SelectionオブジェクトのFindプロパティです。これにより「Selectionオブジェクトを介したFindオブジェクト」が取得されます。このFindオブジェクトに検索条件を設定して検索を実行することになります。
次のExecuteは、Findオブジェクトの条件に従って検索を実行するExecuteメソッドです。
最後のFindText:="又は"では、検索する文字列として「又は」を指定しています。

2行目のSelection.Range.HighlightColorIndex = wdYellowでは、選択範囲のRangeオブジェクトを取得して、黄色のマーキングを行う処理を行っています。

この2行目も簡単に説明しますと、Selection.Rangeは、SelectionオブジェクトのRangeプロパティです。これにより、選択部分のRangeオブジェクトを取得しています。
続くHighlightColorIndexは、前述のとおり、Rangeオブジェクトのマーキングを指定するプロパティです。

サンプルコード2-1-2:全ての文字列を検索してマーキングする例(Selection)
Sub FindBySelection2()
    Do While Selection.Find.Execute("又は")
        Selection.Range.HighlightColorIndex = wdYellow
    Loop
End Sub

(実行結果)
2020-09-24 023356.png
この例では、カーソル位置(選択位置)以降にある全ての文字列を検索して、黄色のマーキングをしています。

1行目のSelection.Find.Execute("又は")では、前例と同様に、指定した文字列「又は」を検索して選択する処理を行っています。
ここで大事なのは、Executeメソッドを実行した場合にはBoolean型(真偽型)の戻り値があることです。検索文字列があればTrueが返され、なければFalseが返されます。
この性質を利用して、戻り値がTrueである限り検索を繰り返すようにしています。
なお、Selectionオブジェクトを介したFindオブジェクトでは、検索を実行する度に選択範囲が移行します。そして、次のループではその選択範囲以降の文字列が検索されるため、このようなシンプルな記述で足りることになります。

2行目は前例と全く同じです。
SelectionオブジェクトからRangeプロパティを取得して、黄色のマーキングを指定しています。

サンプルコード2-1-3:全ての検索文字列を置換してマーキングする例(Selection)

SelectionオブジェクトからFindオブジェクトを取得した場合に、複数の操作を実行するには注意が必要となります。
文字列に対して「置換処理」及び「マーキング処理」を実行した場合の挙動を確認するため、次の文書を例として、3パターンのコードを実行してみます。

(処理前の元文書)
2020-09-24 114933.png
①正常に実行できない例

Sub FindBySelection3_1()
    Do While Selection.Find.Execute("又は")
        Selection.Range.text = "または"
        Selection.Range.HighlightColorIndex = wdYellow
    Loop
End Sub

(実行結果)
2020-09-24 113401.png
このコードの場合、「又は」から「または」への置換は実行されていますが、黄色のマーキング処理は実行されていません。

挙動を確認しますと、まず、1行目のSelection.Find.Execute("又は")の実行により「又は」の部分が選択されます。
次に、2行目のSelection.Range.text = "または"では、置換処理は正常に行われていますが、置換処理実行後にSelectionオブジェクトによる選択が外れてしまいます(選択が外れる理由は分かりませんが、そういう仕様だと理解するしかないと思います。)。
そのため、3行目のSelection.Range.HighlightColorIndex = wdYellowは、どこにも反映されない結果となります。

②正常に実行できる例

Sub FindBySelection3_2()
    Do While Selection.Find.Execute("又は")
        Selection.Range.HighlightColorIndex = wdYellow
        Selection.Range.text = "または"
    Loop
End Sub

(実行結果)
2020-09-24 113648.png
このコードの場合、「又は」から「または」への置換も、黄色のマーキング処理も正常に実行されています。

挙動を確認しますと、2行目のSelection.Range.HighlightColorIndex = wdYellowによるマーキング処理の実行後も、Selectionオブジェクトは「又は」を選択したままの状態を維持します。
そのため、3行目のSelection.Range.text = "または"による置換処理も正常に実行されています。

ただ、このコードはSelectionオブジェクトの性質に左右されるため、このような解決方法では多少の覚束無さがあります。

③Rangeオブジェクトを変数に格納する例

Sub FindBySelection3_3()
    Dim rng As Range
    Do While Selection.Find.Execute("又は")
        Set rng = Selection.Range '選択範囲をRangeオブジェクト変数に格納
        rng.text = "または"
        rng.HighlightColorIndex = wdYellow
    Loop
    Set rng = Nothing
End Sub

(実行結果)
2020-09-24 113648.png
このコードでは、検索された文字の範囲(領域)を、一旦、オブジェクト変数rngに格納した上で処理を行っています。
これにより、Selectionオブジェクトの選択が外れても、影響を受けず複数の処理を実行することができます。

2-2. Rangeオブジェクトから文字列を検索する

Rangeオブジェクトは範囲(領域)を表すオブジェクトです。
このRangeオブジェクトのFindプロパティ(検索機能)を使って、対象文字列の範囲(領域)を取得して処理する例を挙げていきます。

サンプルコード2-2-1:文字列を1つだけ検索して置換する例(Range)
Sub FindByRange1()
    Dim rng As Range
    Set rng = ActiveDocument.Content '先頭から末尾までRangeを設定
    If rng.Find.Execute("又は") Then
        rng.text = "または" '置換処理
        rng.HighlightColorIndex = wdYellow 'マーキング処理
    End If
End Sub

(実行結果)
2020-09-24 124012.png
このコードでは、最初に検索された文字列を置換処理しています。分かりやすいように黄色のマーキングも行っています。

2行目のSet rng = ActiveDocument.Contentでは、オブジェクト変数rngに、現在アクティブな文書(ActiveDocumentプロパティ)の全ての領域(Contentプロパティ)を表すオブジェクトを代入しています(正確には参照を代入)。

3行目のrng.Find.Execute("又は")では、変数rngの持つ領域の中で検索対象文字列「又は」の検索を実行しています。
この検索処理が正常に行われると、rngの持つ領域は検索された文字列「又は」の部分のみに変更されます。
なお、検索文字列が存在しなかった場合は、rng.Find.Execute("又は")の戻り値がFalseになるため、If文の内容は実行されません。

サンプルコード2-2-2:全ての文字列を検索して置換する例(Range)
Sub FindByRange2()
    Dim rng As Range
    Set rng = ActiveDocument.Range(0) '先頭から末尾までRangeを設定
    Do While rng.Find.Execute("又は")
        rng.text = "または" '置換処理
        rng.HighlightColorIndex = wdYellow 'マーキング処理
        Set rng = ActiveDocument.Range(rng.End) '検索文言の末尾以降にRangeを設定
    Loop
End Sub

(実行結果)
2020-09-24 124238.png
この例では、該当する文字列全てを検索して置換処理をしています。

2行目のSet rng = ActiveDocument.Range(0)は、Set rng = ActiveDocument.Contentを書きかえただけです。どちらも文書全体の領域が指定されています。
これは、Rangeメソッドで開始位置の0のみを指定して終了位置の指定を省略していることから、終了位置が自動的に文書の末尾に設定されているためです。

6行目のSet rng = ActiveDocument.Range(rng.End)では、次の検索処理を実行するために、rngの領域を「検索された範囲以降全て」に再設定しています。

サンプルコード2-2-3:While文を使わず全ての文字列を検索して置換する例(Range)
Sub FindByRange3()
    Dim rng As Range
    Set rng = ActiveDocument.Content
    rng.Find.Execute FindText:="又は", ReplaceWith:="または", Replace:=wdReplaceAll
    Set rng = Nothing
End Sub

(実行結果は「サンプルコード2-2-2」と同じであるため省略)

このコードは、FindオブジェクトのReplacementプロパティを使用した例です。
公式ドキュメントのFindオブジェクトのページに掲載されている例をほぼそのまま使用しています。
このような記載をすることで、While文を使用せず、全ての文字列の変換が実行できます。

Findオブジェクトの有する様々なプロパティを使いこなせば、シンプルで効率的なコードが書けそうです(学習中です)。

3. Rangeプロパティを使用して範囲を取得する

先述の「2-1. Selectionオブジェクトから文字列を検索する」で確認しましたように、SelectionオブジェクトではRangeプロパティを用いることでその範囲(領域)を表すオブジェクトを取得することができました。

Rangeプロパティは多くのオブジェクト(Paragraph、Bookmark、Cellなど)で使用できますが、ここでは、Paragraphオブジェクトの例を挙げておきます。

サンプルコード3:段落を指定してマーキングする例
Sub Paragraphs1()
    ActiveDocument.Paragraphs(2).Range.HighlightColorIndex = wdYellow
End Sub

2020-09-24 154130.png
この例では、段落のうち2つ目の範囲を指定してマーキング処理を行っています。

段落は、Paragraphsコレクションで表されます。
このParagraphsコレクションは、Paragraphオブジェクトが複数集まって構成されています。
コレクションが有するParagraphオブジェクトの数は、ActiveDocument.Paragraphs.Countという記述で取得することができます。
コレクションのうち、一つのオブジェクトを取得するには、Paragraphs(2)のように段落番号を指定して取り出します。

このコードでは、.Rangeの記述により、ParagraphオブジェクトのRangeプロパティを使用して、指定段落のRangeオブジェクトを取得しています。
そして、RangeオブジェクトのHighlightColorIndexプロパティを使用して、マーキング処理を実行しているということになります。

なお、2段落目のうち、2文字目と3文字目だけをマーキングするために、.Range(1, 3)と記述しても動作しませんのでご注意ください。
このRangeメソッドは、RangeオブジェクトではなくDocumentオブジェクトにおいて定義されているためです(公式ドキュメントのRangeメソッドを参照)。

おわりに

Word文書をVBAで処理するにあたって、参考となるサイトが少ないため、自分の備忘を兼ねてまとめておきました。
何かしらの参考となれば幸いです。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8