Office関連

選択範囲内で文字列検索を行うWordマクロ

今日は選択範囲内で文字列検索を行うWordマクロについて考えてみます。

下図のように特定の範囲を選択した状態で、その範囲内のみを対象に文字列検索を行いたい場合どうするのか?まずは下記コードのようにSelectionオブジェクトからFindオブジェクトを取得して処理を行ってみます。

Public Sub Sample1()
  Const SearchWords As String = "文書"
  
  With Selection.Find
    .ClearFormatting
    .ClearAllFuzzyOptions
    .Text = SearchWords
    .Replacement.Text = ""
    .Forward = True
    .Wrap = wdFindStop
    .Format = False
    .MatchCase = False
    .MatchWholeWord = False
    .MatchByte = False
    .MatchAllWordForms = False
    .MatchSoundsLike = False
    .MatchWildcards = False
    .MatchFuzzy = False
    Do While .Execute
      Selection.Range.HighlightColorIndex = wdRed
    Loop
  End With
End Sub


すると下図のように文章末まで検索処理が行われてしまいました。
SelectionオブジェクトからFindオブジェクトを取得した場合、文字列がヒットすると選択範囲が変更されてしまうので、これは当たり前の動作と言えます。

次はRangeオブジェクトからFindオブジェクトを取得して処理を行ってみます。

Public Sub Sample2()
  Dim r As Word.Range
  Const SearchWords As String = "文書"
  
  Set r = Selection.Range
  With r.Find
    .ClearFormatting
    .ClearAllFuzzyOptions
    .Text = SearchWords
    .Replacement.Text = ""
    .Forward = True
    .Wrap = wdFindStop
    .Format = False
    .MatchCase = False
    .MatchWholeWord = False
    .MatchByte = False
    .MatchAllWordForms = False
    .MatchSoundsLike = False
    .MatchWildcards = False
    .MatchFuzzy = False
    Do While .Execute
      r.HighlightColorIndex = wdRed
    Loop
  End With
  Set r = Nothing
End Sub

上記コードを実行すると、これもまた先ほどのSelectionオブジェクトからの検索同様に文章末まで検索処理が行われているようです。

では、今度はInRangeメソッドを利用して”ヒットした文字列が選択範囲内だった場合のみ処理を行う“ようにしてみます。
InRangeメソッドは、

メソッドが適用される範囲が引数 Range に指定した範囲内に含まれる場合、True を返します

という説明にあるように、メソッドを実行した対象が指定した範囲内に含まれるかどうかを取得することができるメソッドです。

Public Sub Sample3()
  Dim r As Word.Range
  Const SearchWords As String = "文書"
  
  Set r = Selection.Range
  With r.Find
    .ClearFormatting
    .ClearAllFuzzyOptions
    .Text = SearchWords
    .Replacement.Text = ""
    .Forward = True
    .Wrap = wdFindStop
    .Format = False
    .MatchCase = False
    .MatchWholeWord = False
    .MatchByte = False
    .MatchAllWordForms = False
    .MatchSoundsLike = False
    .MatchWildcards = False
    .MatchFuzzy = False
    Do While .Execute
      '選択範囲内の場合のみ処理実行
      If r.InRange(Selection.Range) Then
        r.HighlightColorIndex = wdRed
      End If
    Loop
  End With
  Set r = Nothing
End Sub

今度は上手くいっているようです。

ですがこの処理も”選択範囲が検索語と同じだった場合“には上手く処理することができません。

それではどうするのか?
これはもう単純に”選択範囲が検索語と同じだった場合“と”それ以外の場合“とに処理を分けてしまえば良いわけです。

Public Sub Sample4()
  Dim r As Word.Range
  Const SearchWords As String = "文書"
  
  Set r = Selection.Range
  '選択範囲が検索語と同じかどうかを判断
  If r.Text = SearchWords Then
    r.HighlightColorIndex = wdRed
  Else
    With r.Find
      .ClearFormatting
      .ClearAllFuzzyOptions
      .Text = SearchWords
      .Replacement.Text = ""
      .Forward = True
      .Wrap = wdFindStop
      .Format = False
      .MatchCase = False
      .MatchWholeWord = False
      .MatchByte = False
      .MatchAllWordForms = False
      .MatchSoundsLike = False
      .MatchWildcards = False
      .MatchFuzzy = False
      Do While .Execute
        '選択範囲内の場合のみ処理実行
        If r.InRange(Selection.Range) Then
          r.HighlightColorIndex = wdRed
        End If
      Loop
    End With
  End If
  Set r = Nothing
End Sub

これでようやく意図した通りに処理できるようになりました。

ただ、このコードにも無駄な部分があります。
それはヒットした場合の処理部分で、選択範囲が検索語と同じだった場合とそれ以外の場合の処理とで同じ処理を二重に書いているため、コードが冗長になってしまっています。
上記コードでは「r.HighlightColorIndex = wdRed」だけなのであまり気になるものではありませんが、これがもっと細かい処理になると、コードも見づらくなり修正する際の手間も増えてしまいます。
それではどうするのか?
こういったときは別途Subプロシージャを用意して処理をまとめてしまえば良いわけです。

Public Sub Sample5()
  Dim r As Word.Range
  Const SearchWords As String = "文書"
  
  Set r = Selection.Range
  '選択範囲が検索語と同じかどうかを判断
  If r.Text = SearchWords Then
    HitProc r
  Else
    With r.Find
      .ClearFormatting
      .ClearAllFuzzyOptions
      .Text = SearchWords
      .Replacement.Text = ""
      .Forward = True
      .Wrap = wdFindStop
      .Format = False
      .MatchCase = False
      .MatchWholeWord = False
      .MatchByte = False
      .MatchAllWordForms = False
      .MatchSoundsLike = False
      .MatchWildcards = False
      .MatchFuzzy = False
      Do While .Execute
        '選択範囲内の場合のみ処理実行
        If r.InRange(Selection.Range) Then
          HitProc r
        End If
      Loop
    End With
  End If
  Set r = Nothing
End Sub

Private Sub HitProc(ByRef r As Word.Range)
'ヒットした場合の処理
  r.Bold = True
  r.Italic = True
  r.Font.Color = wdColorWhite
  r.HighlightColorIndex = wdRed
End Sub

上記コードでは「HitProc」プロシージャによって処理をまとめているので、ヒットした場合の処理はたった1行”HitProc r“とだけ書けば良いわけです。
私もWordマクロに精通しているわけではなく、本当はもっと効率の良い方法があるのかもしれませんが、とりあえずこれで目的の処理ができるようになりました。

最後にもう一点、Word 2007で追加されたHitHighlightメソッドの場合はInRangeメソッドで範囲判定を行う必要なく、選択範囲のみを処理することができます。

Public Sub Sample6()
  Dim r As Word.Range
  Const SearchWords As String = "文書"
  
  Set r = Selection.Range
  With r.Find
    .ClearFormatting
    .ClearAllFuzzyOptions
    .Text = ""
    .Replacement.Text = ""
    .Forward = True
    .Wrap = wdFindStop
    .Format = False
    .MatchCase = False
    .MatchWholeWord = False
    .MatchByte = False
    .MatchAllWordForms = False
    .MatchSoundsLike = False
    .MatchWildcards = False
    .MatchFuzzy = False
    .HitHighlight SearchWords
  End With
  Set r = Nothing
End Sub

HitHighlightメソッドはヒットした文字列を強調表示するだけなので、ヒットした文字列部分をオブジェクトとして取得できるExecuteメソッドとは用途が大きく異なるわけですが、こうした細かい動作の違いを覚えておくと、どこかで役に立つかもしれないですね。

2016/09/12 追記:
Collapseメソッドを入れることで検索語判定を入れることなく処理できることを教えていただきました。
If文がなくなるので大分スマートになりますね!

Public Sub Sample7()
  Dim r As Word.Range
  Const SearchWords As String = "文書"
   
  Set r = Selection.Range
  r.Collapse wdCollapseStart '<--追加
  With r.Find
    .ClearFormatting
    .ClearAllFuzzyOptions
    .Text = SearchWords
    .Replacement.Text = ""
    .Forward = True
    .Wrap = wdFindStop
    .Format = False
    .MatchCase = False
    .MatchWholeWord = False
    .MatchByte = False
    .MatchAllWordForms = False
    .MatchSoundsLike = False
    .MatchWildcards = False
    .MatchFuzzy = False
    Do While .Execute
      '選択範囲内の場合のみ処理実行
      If r.InRange(Selection.Range) Then
        r.HighlightColorIndex = wdRed
      End If
    Loop
  End With
  Set r = Nothing
End Sub

セルの行数をカウントするWordマクロ前のページ

右クリックメニューを非表示にするExcelマクロ次のページ

関連記事

  1. Office関連

    Office 2016で“ヤツ”を召喚してみた。

    4月1日にMicrosoft Office公式アカウント、MSOffi…

  2. Office関連

    Visio Onlineで図の作成・編集ができるようになりました。

    しばらくVisio Onlineを使っていなかったので気が付かなかった…

  3. Office アドイン

    [Officeアドイン]アドイン コマンド(Add-In Commands)の紹介(2)

    昨年末に書いた記事で「アドイン コマンド」を紹介しているのですが、知ら…

  4. Office関連

    [PowerPoint]図やスライドをSVGとして保存する機能が追加されました。

    ※ 下記情報はInsider版のOfficeを元にしています。バージョ…

  5. Office関連

    VBA Word 97/98ハンドブックを購入しました。

    ブックオフにあった「VBA Word 97/98ハンドブッ…

  6. Office関連

    Trello APIを使ってカードを投稿するVBAマクロ

    以前Fiddlerを使ってTrello APIを実行する記事を書きまし…

コメント

    • マナ
    • 2016年 9月 10日 6:59pm

    いつも参考にさせていただいています。
    InRangeメソッドでの判定、とても勉強になりました。

    古い記事へのコメントで恐縮ですが

    >”選択範囲が検索語と同じだった場合“

    r.Collapse wdCollapseStart

    を実行することでも、上手くいくみたいです。

    • > マナさん

      当ブログ管理人のきぬあさです。

      > r.Collapse wdCollapseStart

      なるほど。
      たしかにCollapseメソッドを入れることでIf判定させる必要がなくなりますね!
      この度は有益な情報を教えていただき、誠にありがとうございました。

Time limit is exhausted. Please reload CAPTCHA.

※本ページはプロモーションが含まれています。

Translate

最近の記事

アーカイブ

PAGE TOP