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

喜欢而已 提交于 2019-12-07 11:37:08

问题


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?


回答1:


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



来源:https://stackoverflow.com/questions/29838368/cant-get-recursive-range-find-in-word-vba-to-work

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!