Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations John Tel on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Find And Replace Macro in WOrd VBA 1

Status
Not open for further replies.

DJB1954

Technical User
Aug 9, 2006
3
GB
I am relatively new to Word VBA and have a problem with my logic. I want to prompt a user for two numbers, then search the text for those two numbers enclosed within square brackets and highlighted (in fact I have just realised that the condition "highlighted" is sufficient but that is not relevant to my problem.

I have tried to construct a Do..While loop to run the Find and Replace until Low Number = High Number. I have a number of problems - the loop is endless at the moment and sometimes I get a mismatch Error 13 caused I think by the type of variable i have chosen to store lownum and hinum (i've been round this so many times, I am now confused). Any help would be VERY gratefully accepted. I copy the code below.

Sub Step4_1()

' Step4_1 Macro


Dim Message, Title, Default, lowNum As String, hiNum As String, check
check = True: lowNum = 0: hiNum = 0 'Initialise variables
Message = "Enter the lower section number to remove - DO NOT ENTER THE BRACKETS" 'Set prompt
Title = "User Input Low Section Number" ' Set title.
Default = "0" ' Set default.
' Display message, title, and default value.
lowNum = InputBox(Message, Title, Default)

Message = "Enter the upper section number to remove - DO NOT ENTER THE BRACKETS" 'Set prompt
Title = "User Input High Section Number" ' Set title.
Default = "0" ' Set default.
' Display message, title, and default value.
highNum = InputBox(Message, Title, Default)

Do ' Outer loop
' We now have user-input values for lownum and hinum. The idea now is to increment lownum until it matches hinum and for as
'long as this loop lasts, to perform a find and replace, feeding the current value of lownum into the findtext.



With Selection.Find
Do While check = True ' Inner loop.
.Text = "[" + lowNum + "],"
.Highlight = True
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False


Selection.Find.Execute Replace:=wdReplaceAll

'Exit Do ' Exit inner loop.
If lowNum = hiNum Then
check = False ' Set value of flag to false.

End If
lowNum = lowNum + 1 'Increment lowNum counter.
Loop
End With

Loop Until check = False ' Exit Outer loop immediately
End Sub
 

The reason your loop never ends is that sometimes you use hiNum and sometimes highNum. If you use "Option Explicit" (code as the first line of a module or set "Require Variable Declaration" under Tools > Options > Editor to have it defaulted for all new modules) then the compiler will highlight this kind of error.

Now let's try and see the wood from the trees ...

First of all you are actually repeating a lot of code that doesn't need doing every time. Properties of the Selection.Find object are persistent so you can move the setting of most of them outside the loop(s):
Code:
    With Selection.Find
        .Highlight = True
        .Replacement.Text = ""
        .Forward = True
        .Wrap = wdFindContinue
        .Format = True
        .MatchCase = False
        .MatchWholeWord = False
        .MatchWildcards = False
        .MatchSoundsLike = False
        .MatchAllWordForms = False
    End With

Do         ' Outer loop     
    With Selection.Find
    Do While check = True  ' Inner loop.
        .Text = "[" + lowNum + "],"
    
      Selection.Find.Execute Replace:=wdReplaceAll
    
    'Exit Do    ' Exit inner loop.
        If lowNum = hiNum Then
            check = False ' Set value of flag to false.
    
        End If
        lowNum = lowNum + 1            'Increment lowNum counter.
    Loop
    End With
    
Loop Until check = False  ' Exit Outer loop immediately
Now all that's left inside the loop is setting the text for the Find, executing the find, incrementing the counter and setting the loop control.

The text can be specified on the execute:
Code:
Do         ' Outer loop     
    With Selection.Find
    Do While check = True  ' Inner loop.
    
      Selection.Find.Execute FindText:="[" + lowNum + "],", _
                             Replace:=wdReplaceAll
    
    'Exit Do    ' Exit inner loop.
        If lowNum = hiNum Then
            check = False ' Set value of flag to false.
    
        End If
        lowNum = lowNum + 1            'Increment lowNum counter.
    Loop
    End With
    
Loop Until check = False  ' Exit Outer loop immediately
Now the With is doing nothing and can go:
Code:
Do         ' Outer loop     
    Do While check = True  ' Inner loop.
    
      Selection.Find.Execute FindText:="[" + lowNum + "],", _
                             Replace:=wdReplaceAll
    
    'Exit Do    ' Exit inner loop.
        If lowNum = hiNum Then
            check = False ' Set value of flag to false.
    
        End If
        lowNum = lowNum + 1            'Increment lowNum counter.
    Loop
    
Loop Until check = False  ' Exit Outer loop immediately
.. which highlights the fact that you have two loops for no good purpose and can get rid of one of them:
Code:
Do While check = True  ' Inner loop.
    
      Selection.Find.Execute FindText:="[" + lowNum + "],", _
                             Replace:=wdReplaceAll
    
    'Exit Do    ' Exit inner loop.
        If lowNum = hiNum Then
            check = False ' Set value of flag to false.
    
        End If
        lowNum = lowNum + 1            'Increment lowNum counter.

Loop Until check = False  ' Exit Outer loop immediately
Next, instead of having a Do Loop, a For ... Next construct will incorporate the counter into the loop mechanism and you can get rid of the check flag:
Code:
For lowNum = lowNum to hiNum
    
      Selection.Find.Execute FindText:="[" + lowNum + "],", _
                             Replace:=wdReplaceAll
    
    'Exit Do    ' Exit inner loop.

Next lowNum
Tidying up and removing the commented out Exit leaves this:
Code:
[blue]With Selection.Find
    .Highlight = True
    .Replacement.Text = ""
    .Forward = True
    .Wrap = wdFindContinue
    .Format = True
    .MatchCase = False
    .MatchWholeWord = False
    .MatchWildcards = False
    .MatchSoundsLike = False
    .MatchAllWordForms = False
End With

For lowNum = lowNum to hiNum
    
    Selection.Find.Execute FindText:="[" + lowNum + "],", _
                           Replace:=wdReplaceAll
    
Next lowNum[/blue]
Now, correcting the highNum typo is all that you should need to make it work:
Code:
Message = "Enter the upper section number to remove - DO NOT ENTER THE BRACKETS"    'Set prompt
Title = "User Input High Section Number"    ' Set title.
Default = "0"    ' Set default.
' Display message, title, and default value.
[red]hiNum[/red] = InputBox(Message, Title, Default)

You would also be better:
(a) validating your input,
(b) using numeric variables, and
(b) using "&" rather than "+" for concatenation.

Enjoy,
Tony

--------------------------------------------------------------------------------------------
We want to help you; help us to do it by reading this: Before you ask a question.

Professional Office Developers Association
 
Tony,
Above all, many thanks for this excellent, considered and detailed response which has taught me much. I worked through your various steps and now understand the code much better. The code runs - only problem is that it doesn't perform as I anticipated. I obviously have another problem with my logic. Let me try to explain what I want to achieve.
Find all instances of highlighted text with a value of "[number of from 1 to 3 digits equal to lownum's current value but always less than hinum],".

What actually happens is that it finds a value of "0]"i.e. just a zero followed by closing square bracket but not the first such instance.

My (or rather your) much more elegant code below. You can see that I have commented out the Replace to test the Find logic and have also experimented with converting lownum to a string - makes no difference. I would be enormously grateful if you or someone else could spot my poor logic.

Denis

Code:
Sub Step4_1()

' Step4_1 Macro
' Macro based upon Step1_2 9/8/2006 by Denis Brennan
'
Dim Message, Title, Default, lowNum As Integer, hiNum As Integer, lowNumStr As String
check = True: lowNum = 0: hiNum = 0  'Initialise variables

Message = "Enter the lower section number to remove - DO NOT ENTER THE BRACKETS"    'Set prompt
Title = "User Input Low Section Number"    ' Set title.
Default = "0"    ' Set default.'  Changed the default value to 9 to test if it is only ever searching for default value. No effect!
' Display message, title, and default value.
lowNum = InputBox(Message, Title, Default) 'need to look at adding validation code to these _
                        input boxes to ensure values are OK and not going to screw things up

Message = "Enter the upper section number to remove - DO NOT ENTER THE BRACKETS"    'Set prompt
Title = "User Input High Section Number"    ' Set title.
Default = "0"    ' Set default.
' Display message, title, and default value.
hiNum = InputBox(Message, Title, Default)

'Code now much more elegant (thanks to Tony at tek-tips). And the Module runs but does NOT perform expected replaces. Why?

     
    With Selection.Find
        .Highlight = True            ' this highlight is correct. Query? Should (or can) it go in the execute below?
        '.Replacement.Text = ""
        .Forward = True
        .Wrap = wdFindContinue
        .Format = True
        .MatchCase = False
        .MatchWholeWord = False
        .MatchWildcards = False
        .MatchSoundsLike = False
        .MatchAllWordForms = False
    End With

For lowNum = lowNum To hiNum
lowNumStr = CStr(lowNum) ' this converts lowNum to a string and this is then used in the FindText below. This still does _
                            not work so I have now commented out the Replacement to see if Find works. Find always finds _
                            a highlighted instance of 0, in the last digit position before closing square bracket, but it _
                            also ignores earlier instances of this in the document. Why? _
                            Is it that the value of lownum is not being set correctly? Need to look at this. _
                            Alternative thought:  is it a problem with the search range?
    
        Selection.Find.Execute FindText = "[" & lowNum & "]," ', _
                                Replace:=wdReplaceAll
    
        'Selection.Find.Execute FindText = lowNumStr ' Changing this to look for just lownum has no impact - now try it to look for lowNumStr - no effect
    
    
Next lowNum
        
    
End Sub
 
Hi Denis,

Sorry I didn't check in yesterday and see your response.

There is a possibility I can think of immediately - you have .Format set to True. This means that Word will only look for text in the specified format. As you have not specified a format it will continue to use whatever format happened to have last been used - for example if the previous search (perhaps via the UI) was for bold text it will now only be looking for bold text. You should explicitly specify False to remove this possibility.

Unfortunately that doesn't explain why it should find something which doesn't match the criteria - are you sure it's finding it and that the selection wasn't already on it before you started? Combined with a Format setting it is possible that nothing is found and the Selection isn't therefore changed.

Enjoy,
Tony

--------------------------------------------------------------------------------------------
We want to help you; help us to do it by reading this: Before you ask a question.

Professional Office Developers Association
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top