简体   繁体   中英

Can't get recursive Range.Find in Word VBA to work

I've been flailing at this for a while and can't seem to come up with a solution. I have to search through a document from start to finish with a wildcard search for custom mark-up. For the sake of the question, we'll say {something} When I find a specific match, it gets replaced with the contents of another string which can also contain mark-up. The mark-up has to be replaced in the order it will appear in the final document AND I have to know the recursion level that each replacement was made at.

This is basically what I came up with. Note that the ProcessReplacement function is contrived for the example - the text gets replaced by an external program:

Option Explicit

Private replaced As Integer

Public Sub Demo()

    Dim pos As Range

    Set pos = ActiveDocument.Content
    replaced = 0
    pos.Text = "{fizz}{fizz}{more}{buzz}{buzz}"
    Expand pos

End Sub

Private Sub Expand(incoming As Range, Optional depth = 1)

    Dim sub_range As Range
    Dim end_pos As Long

    end_pos = incoming.End
    With incoming.Find
        .ClearFormatting
        .MatchWildcards = True
        .Forward = True
        .Wrap = wdFindStop
    End With

    Do While incoming.Find.Execute("\{*\}")
        If incoming.Start < incoming.End Then
            Debug.Print "Replaced " & incoming.Text & " at " & depth
            end_pos = end_pos + ProcessReplacement(incoming)
            Set sub_range = incoming.Duplicate
            Expand sub_range, depth + 1
            incoming.End = end_pos
            incoming.Start = sub_range.End - 1
        End If
    Loop

End Sub

Private Function ProcessReplacement(replacing As Range) As Long
    Dim len_cache As Long

    len_cache = Len(replacing.Text)

    If replacing.Text = "{more}" Then
        replacing.Text = "{foo}{evenmore}{bar}"
    ElseIf replacing.Text = "{evenmore}" Then
        'This kind of works.
        replacing.Text = "{fizzbuzz} "
        'This doesn't work at all.
'        replacing.Text = "{fizzbuzz}"
    Else
        replaced = replaced + 1
        replacing.Text = "<" & replaced & ">"
    End If

    ProcessReplacement = Len(replacing.Text) - len_cache
End Function

The first issue is that I can't figure how to keep the .Find.Execute confined to the correct Range. This is what the document and output look like (with the space after {fizzbuzz}- more on that later):

Document text: <1><2><3><4> <5><6><7>
Output:
Replaced {fizz} at 1
Replaced {fizz} at 1
Replaced {more} at 1
Replaced {foo} at 2
Replaced {evenmore} at 2
Replaced {fizzbuzz} at 3
Replaced {bar} at 2
Replaced {buzz} at 2    <---This was outside of the range at that depth.
Replaced {buzz} at 1

If I take the space out after {fizzbuzz}, it doesn't even get matched, even though I confirmed in the watch window that it is basically the contents of the range when the function recurses after its replacement. Output without the space:

Document text: <1><2><3>{fizzbuzz}<4><5><6>
Output:
Replaced {fizz} at 1
Replaced {fizz} at 1
Replaced {more} at 1
Replaced {foo} at 2
Replaced {evenmore} at 2
Replaced {bar} at 3  <---No clue how this happens - wdFindStop is ignored.
Replaced {buzz} at 3
Replaced {buzz} at 3

Expected output (with or without spaces):

Document text: <1><2><3><4><5><6><7>
Output:
Replaced {fizz} at 1
Replaced {fizz} at 1
Replaced {more} at 1
Replaced {foo} at 2
Replaced {evenmore} at 2
Replaced {fizzbuzz} at 3
Replaced {bar} at 2
Replaced {buzz} at 1
Replaced {buzz} at 1

Anybody see anything that I'm missing?

Word's Find behavior is very odd.

Among other peculiarities, if your search text is an exact match for the Range 's text, then the Wrap option is ignored, and the search range is redefined as per this article :

When the Find object .Execute method determines that the thing to find exactly matches the search range, the search range is dynamically redefined. The new search range starts at the end of the old search range and ends at the end of the document (or targeted storyRange). Processing continues in the redefined range.

That's why the {fizzbuzz} (with the trailing space) works - it's not an exact match.

You'll need to adapt your code to handle:

  1. Range.Text is an exact match for the wildcard search, and/or:
  2. After calling Execute , check that the Range 's start is before the expected end.

You can see the Range changes in action by adding a Range.Select statement before and after every Execute call and before and after every Text assignment

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM