Wordにおける構成要素とRange

カテゴリー名: [WordVBAとOLE 基本構成とその操作

2016/10/09

Word文書におけるオブジェクトの集合(Paragraphs, Sentences, Words など)と Range について解説。

プログラムのサンプルでは、検索とRangeの関係を取り上げます。
また、見出しスタイルの設定と、中央揃え・右揃えの配置の記述も含まれます。

素材として用いる sample01.txt は「走れメロス」の冒頭部分。


《このページの目次》


    

1. 概要

 Word文書には様々な構成要素があります。しかも、それら要素が複数あるのが一般的です。

  段落の集合は ActiveDocument.Paragraphs で表現します。

 段落の個数(総数)は ActiveDocument.Paragraphs.Count で知ることができます。

 最初の段落は ActiveDocument.Paragraphs.First で得られます。
これは ActiveDocument.Paragraphs(1) と同じです。

 最後の段落は ActiveDocument.Paragraphs.Last です。

 Paragraphs を Sentences にすれば「文」に関する情報を得ることになり、
Words にすれば「単語」、Characters にすれば「文字」です。

 そして、これら構成要素は、各々個別に扱うだけでは文書処理できません。

 第1〜第3の単語を一括して太字にしたいとか、フォントサイズを大きくしたいといった場合、構成要素を超えた範囲指定ができないと不便です。

 そうしたときに用いるが Range です。隣接する範囲を示します。

 では、どのようにして Range を指定したらいいか……

    

(1) 文書中での位置を知るための Start, End

  ActiveDocument.Content.Start は、文書本文のスタート位置を返します。整数値 0 が返されます。

 ActiveDocument.Content.End は、文書本文の終了位置を返します。

 この End の値は、本文中の文字数と同じ値になるようです。バイト数ではなく文字数です。

 半角文字も全角文字も1文字としてカウントします。改行コードも1文字としてカウントした値になるようです。

    

 ActiveDocument.Sentences(1).Start とか、ActiveDocument.Words(1).End のようにして、センテンスや単語の位置を知ることができます。

 こうした Start, End の値は、Rangeを設定するときの範囲指定に使います。

 Sentences(1) の Start, End がそれぞれ 0, 7 だった場合、
ActiveDocument.Range(0, 7) は、ActiveDocument.Sentences(1) と同じ部分を指し示します。

 第1センテンスの Start が 0、第3センテンスの End が 25 だとすると、
ActiveDocument.Range(0, 25) は、第1〜3の三つのセンテンスの一連の範囲を指し示します。

 このようにして範囲指定したRangeオブジェクトには、Boldプロパティを適用して太字にしたり、Font.Size を適用して文字の大きさを変更したりといったことができます。

ActiveDocument.Range(0, 25).Bold = True
ActiveDocument.Range(0, 25).Font.Size = 14

 上のような記述ができます。

    

 ここで、マクロ記述の例を掲げておきます。

Sub Macro1()
    Dim rng As Range
    ActiveDocument.Content.Delete  ' 本文部分を全クリア
    Selection.InsertFile ActiveDocument.Path & "\sample01.txt"
    Set rng = ActiveDocument.Range(0, 25)
rng.Bold = True  ' 太字に設定
End Sub

    

(2) 文書の構成要素とRangeオブジェクト

 次の文書の構成要素は、いずれもRangeオブジェクトです。

 したがって、同じプロパティとメソッドを持ちます。

 上記の整数値は例として上げただけです。場合に応じた適当な整数値を指定します。

 上は、いずれもRangeオブジェクトなので Content.Range というような記述はできません。というより、そんな記述をする必要がありません。

    

◇ Paragraph と Range

 Paragraph(1) といった一つの段落は、Rangeオブジェクトではありません。

 一つの段落をRangeオブジェクトとして扱うときは、Paragraphs(1).Range と書きます。

 段落に書かれている文字列は、Paragraphs(1).Range.Text で得ることができます。末尾は改行 vbCr になっているはずです。例外的に vbCrLf になる場合がありますが、通常は vbCr です(本ページの最後の項を参照)。

 段落の位置を示す Start, End は、それぞれ下のように書いて取得します。

ActiveDocument.Paragraphs(1).Range.Start
ActiveDocument.Paragraphs(1).Range.End

 段落は、一定の範囲を示すだけでなく、文字の配置、インデント、ワードラップなど様々な設定を行う単位でもあることから Rangeオブジェクトではないのだと推測します。

    

◇ Selection と Range

 選択されている部分を示す Selection は、Rangeオブジェクトと共通点が少なくありませんが、Rangeオブジェクトではありません。

 Start, End, Font あるいは Copy, Cut, Paste, Delete, InsertFile などは、SelectionでもRangeでも使えます。

 一方、Bold, Italic といったプロパティは、Rangeでは使えますが、Selectionでは使えません。使えてもいいような気がするのですが……

 Rangeオブジェクトで使えるプロパティやメソッドを利用したいときは、
Selection.Range.Bold = True のように記述します。


    

(3) Start, Endに関する注意点

 Rangeオブジェクトの Start, End の値についてちょっと注意すべき点があります。

 文書中の Sentence を一つずつ順番に見ていったとします。その Start, End の値を見ます。

 すると、Start, Endの組が、たとえば下のようになります。

[0, 7]  [7, 15]  [15, 25]  [25, 52]

 第1のEndと第2のStartの値が同じです。

 自然数を5個ずつのグループに区切るとすれば、
[1, 5] [6, 10] [11, 15] …… となります。

 しかし、Start, Endの値は、自然数を区切るときのようにはなっていません。

 あるRangeオブジェクトのEndの値は、その直後のオブジェクトのStartと等しいという関係になっています。

 この仕様のため、たとえば ActiveDocument.Range(0, 0) とすると、1文字も含まないRangeオブジェクトを生成できます。文書先頭を指し示します。

 このようにして文書の先頭を指定した上で文字やファイルを挿入すれば、何よりも前に挿入できます。

 ともあれ、Rangeオブジェクトの始点と終点は Start, End で知ることができ、
また、Rangeオブジェクトの範囲指定は Start, Endを用いて行うことができます。

    

 Start, End の値が、どこを起点にしているかも気になる点です。

 第4段落の中の第2センテンスの Start, End は、次の記述で得ることができます。

ActiveDocument.Paragraphs(4).Range.Sentences(2).Start
ActiveDocument.Paragraphs(4).Range.Sentences(2).End

 この場合、Start, End の値は、第4段落の先頭を起点にしているのか、あるいは、文書本文の先頭を起点にしているのか気になります。

 調べてみると、文書本文の先頭を起点にしていました。いわば相対位置ではなく絶対位置です。

Sub Macro1()
    Dim rng As Range
    Dim RStart As Integer, REnd As Integer
    ActiveDocument.Content.Delete  ' 本文部分を全クリア
    Selection.InsertFile ActiveDocument.Path & "\sample01.txt"
    RStart = ActiveDocument.Paragraphs(4).Range.Sentences(2).Start
    REnd = ActiveDocument.Paragraphs(4).Range.Sentences(2).End
    Set rng = ActiveDocument.Range(RStart, REnd)
    MsgBox ActiveDocument.Paragraphs(4).Range.Sentences(2).Text
    MsgBox rng.Text
End Sub

 上のマクロで2度の MsgBox 出力が行われますが、同じ出力になります。

 Start, End が絶対位置を返すため同じになります。

    

◇ メソッドとしてのRangeと、プロパティとしてのRange

 これまで Range と書いてきたものには、実はメソッドとプロパティの2種類があります。

 範囲指定することによりRangeオブジェクトを生成する場合の記述
ActiveDocument.Range(0, 7) のRangeは、オブジェクトを生成するためのメソッドです。

 このRangeメソッドは、ドキュメントオブジェクトでしか使えないようです。

 なので、ActiveDocument.Paragraphs(4).Range(0, 7) のいう書き方はできません。

 ActiveDocument.Paragraphs(4).Range のRangeはプロパティです。一つの段落からRangeオブジェクトを引き出すためのプロパティです。メソッドではありません。

 引き出された値はRangeオブジェクトですから、Rangeオブジェクトで使えるメソッドやプロパティを適用できます。

 ややこしいですが、要は、Range(0, 7) のような範囲指定してRangeオブジェクトを生成するやり方は、ドキュメントオブジェクトでしか使えないということです。


    

2. VBAマクロ

 Rangeオブジェクトを利用する例として検索を取り上げてみます。

 第4段落と第5段落を対象にして「メロス」という文字列を検索し、みつかったらその3文字を斜体にします。

 検索対象範囲(第4段落の先頭から第5段落の末尾)を設定するのにRangeを使います。

 変数rngにRangeオブジェクトが代入されている場合、「メロス」を検索するには次のようにします。

rng.Find.Execute("メロス")

 目的の文字列がみつかった場合は、上の ExecuteメソッドがTrueを返します。

    

 そのほか、見出しスタイルの設定、中央揃え・右揃えの配置も行います。

 本文全体を標準スタイルにした上で、第1段落を「見出し 1」のスタイルにします。文字サイズが少し大きくなるなど標準スタイルと違うものになります。

 それから、第1段落を中央揃え、第2段落を右揃えにします。

 以下、VBAマクロです。

    

△ macro02.txt

 1' Wordにおける構成要素とRange:スタイル・配置・検索
 2Sub Macro1()
 3    Dim rng As Range
 4    Dim SearchEnd As Integer
 5    With ActiveDocument
 6        .Content.Delete  ' 本文部分を全クリア
 7        .Range(0,0).InsertFile .Path & "\sample01.txt"
 8        .Content.Style = wdStyleNormal  ' 標準スタイルを適用
 9        .Paragraphs(1).Style = wdStyleHeading1  ' 見出し1のスタイル
10        .Paragraphs(1).Alignment = wdAlignParagraphCenter  ' 中央揃え
11        .Paragraphs(2).Alignment = wdAlignParagraphRight  ' 右揃え
12        SearchEnd = .Paragraphs(5).Range.End  ' 検索範囲の終点
13        Set rng = .Range(.Paragraphs(4).Range.Start, SearchEnd)
14        Do While rng.Find.Execute("メロス")
15            rng.Italic = True
16            rng.SetRange rng.End, SearchEnd
17        Loop
18    End With
19End Sub

    

 rng.Find.Execute で検索し、みつかった時は rng が自動的に変化します。

 rngは、みつかった「メロス」の3文字を指し示すRangeオブジェクトになります。

 なので、rng.Italic = True とすれば「メロス」の3文字が斜体になります。

 次の検索を行う前に検索対象範囲を再設定します。それを行うのが
rng.SetRange rng.End, SearchEnd です。SetRangeが範囲再設定のためのメソッドです。

 「メロス」が対象範囲内で見つからなくなるまで検索が繰り返されます。

 見出しスタイルの設定、中央揃え・右揃えの配置については、一つの記述例として参考にしてもらえればと思います。

目次に戻る


    

3. OLEを利用するVBScript

 前述のVBAマクロと同じ処理をするVBScriptを掲げます。

 生成される Test01.doc は、第4段落と第5段落の「メロス」が斜体になっているはずです。

△ vovWD02.vbs

 1' Wordにおける構成要素とRange:スタイル・配置・検索
 2Option Explicit
 3Dim FSO, DocPath
 4Dim WDapp, DOCobj, rng, SearchEnd
 5Const wdFormatDocument = 0
 6Const wdStyleNormal = -1
 7Const wdStyleHeading1 = -2
 8Const wdAlignParagraphCenter = 1
 9Const wdAlignParagraphRight = 2
10
11Set FSO = CreateObject("Scripting.FileSystemObject")
12DocPath = FSO.GetAbsolutePathName("Test01.doc")
13If (FSO.FileExists(DocPath) = True) Then FSO.DeleteFile(DocPath)
14Set WDapp = CreateObject("Word.Application")  ' Wordの起動
15WDapp.Visible = True  ' Wordを見える状態に
16Set DOCobj = WDapp.Documents.Add()
17With DOCobj
18    .Content.Delete  ' 本文を全クリア
19    .Range(0,0).InsertFile FSO.GetAbsolutePathName("sample01.txt")
20    .Content.Style = wdStyleNormal  ' 標準スタイルを適用
21    .Paragraphs(1).Style = wdStyleHeading1  ' 見出し1のスタイル
22    .Paragraphs(1).Alignment = wdAlignParagraphCenter  ' 中央揃え
23    .Paragraphs(2).Alignment = wdAlignParagraphRight  ' 右揃え
24    SearchEnd = .Paragraphs(5).Range.End  ' 検索範囲の終点
25    Set rng = .Range(.Paragraphs(4).Range.Start, SearchEnd)
26    Do While rng.Find.Execute("メロス")
27        rng.Italic = True
28        rng.SetRange rng.End, SearchEnd
29    Loop
30End With
31DOCobj.SaveAs DocPath, wdFormatDocument
32WDapp.Quit

目次に戻る


    

4. JScript

 先のVBScriptと同じ処理を行うJScriptは下のようになります。

△ vovWD02.js

 1// Wordにおける構成要素とRange:スタイル・配置・検索
 2var fso, docPath;
 3var WdApp, docObj, rng, searchEnd;
 4var wdFormatDocument = 0;
 5var wdStyleNormal = -1;
 6var wdStyleHeading1 = -2;
 7var wdAlignParagraphCenter = 1;
 8var wdAlignParagraphRight = 2;
 9
10fso = WScript.CreateObject("Scripting.FileSystemObject");
11docPath = fso.GetAbsolutePathName("Test01.doc");
12if (fso.FileExists(docPath))  fso.DeleteFile(docPath);
13WdApp = WScript.CreateObject("Word.Application");  // Wordの起動
14WdApp.Visible = true;  // Wordを見える状態に
15docObj = WdApp.Documents.Add();
16with (docObj) {
17    Content.Delete();  // 本文を全クリア
18    Range(0,0).InsertFile(fso.GetAbsolutePathName("sample01.txt"));
19    Content.Style = wdStyleNormal;  // 標準スタイルを適用
20    Paragraphs(1).Style = wdStyleHeading1;  // 見出し1のスタイル
21    Paragraphs(1).Alignment = wdAlignParagraphCenter;  // 中央揃え
22    Paragraphs(2).Alignment = wdAlignParagraphRight;  // 右揃え
23    searchEnd = Paragraphs(5).Range.End;  // 検索範囲の終点
24    rng = Range(Paragraphs(4).Range.Start, searchEnd);
25    while(rng.Find.Execute("メロス")) {
26        rng.Italic = true;
27        rng.SetRange(rng.End, searchEnd);
28    }
29}
30docObj.SaveAs(docPath, wdFormatDocument);
31WdApp.Quit();

目次に戻る


    

5. テキストファイルの取り込みと改行コード

 段落(Paragraph)は、改行コードでおわっている一連の文字列です。

 その改行コードに少し引っかかったのでメモとして残しておきます。

 以下に出てくる vbCr を別の書き方にすれば Chr(13) です。
vbCrLf は、Chr(13) & Chr(10) の2桁です。

    

(1) InsertFileメソッドを利用したときの改行コード

 段落の改行コードは、通常 vbCr ですが、InsertFileメソッドでテキストファイルを読み込んだときは vbCrLf の2桁になっています。

 といっても、読み込んだ最後の方は vbCr になっていることがあります。
なんだか統一性がありません。

 しかも、InsertFileで読み込んだあと、Word文書として保存したとします。
そして、そのWord文書を改めてオープンすると、どの段落も改行コードが vbCr になっています。vbCrLfの2桁ではなくなっています。

 それなら、InsertFileを実行した直後に vbCrLf をすべて vbCr に一括変換してしまえばいいのではないかと思って試してみると、変換前の段落の個数が42あったのに、変換後は2に激減したりします。

 この「vbCrLf → vbCr」の一括変換は、他の処理にも悪影響を及ぼしそうな感じがします。やらない方がいいかもしれません。

 改行コードを気にする必要がなければ特段のことはありませんが、気にしなければならない場合は、扱いにくいので注意して下さい。

    

 段落の文字を比較の対象にする場合、たとえば

If .Paragraphs(1).Range.Text = "あいうえお" Then

 上のような記述だと「一致」にはなりません。"あいうえお" & vbCr にしないとダメだと思います。

 しかし、InsertFileで読み込んだ部分については vbCr を vbCrLf にしないと「一致」しません。

 比較処理など改行コードを持ち出さなければならないときは InsertFileメソッドを使わないのが賢明でしょうか。

    

(2) テキストファイルを取り込む他の方法

 テキストファイルを読み込んで、ある変数に文字列として取り込み、
その文字列を InsertAfter または InsertBefore で取り込めば、
改行コードは vbCr になります。

 vbCr なのか vbCrLf なのかで迷う必要はありません。

 VBAマクロの形でサンプルを掲げてみます。

    

Sub Macro1()
    With ActiveDocument
        .Range(0,0).InsertAfter ReadTextFile("test.txt")
        If .Paragraphs(1).Range.Text = "あいうえお" & vbCr Then
            MsgBox "一致しました!"
        Else
            MsgBox "一致しません."
        End If
    End With
End Sub

Function ReadTextFile(ByVal FileName As String)
    Dim FSO As Object, FileObj As Object
    Dim FilePath As String, MyStr As String
    Set FSO = CreateObject("Scripting.FileSystemObject") 
    FilePath = FSO.GetAbsolutePathName(FileName)
    MyStr = ""
    If FSO.FileExists(FilePath) Then
        Set FileObj = FSO.OpenTextFile(FilePath)
        MyStr = FileObj.ReadAll()
        FileObj.Close
    End If
    Set FSO = Nothing
    Set FileObj = Nothing
    ReadTextFile = MyStr
End Function

    

 テキストファイルを取り込んで変数に代入する処理は、ReadTextFile という関数を設けて、そこで行っています。

 InsertFileメソッドを使う場合よりも手間が増えますが、改行コードに関する迷走を避けられます。

〜 以上 〜