Loop through each word in doc

D

Daniel Bonallack

Hi, I'm just really looking for some help to get started. I'd like to loop
through every word in a document, and do something to that word if a certain
condition is met. Would you tell me where I'm going wrong?

Thanks
Daniel

Sub LoopWord()

Dim myDoc As Document

Set myDoc = ActiveDocument

For Each Word In myDoc
If InStr(1, Word, myPre) > 0 Then
Word = Replace(Word, "xx_", "yy_")
End If
Next Word

End Sub
 
J

Jay Freedman

Where to start... This is wrong in many ways. It's even the completely wrong
approach, but first let me explain what's happening inside your code.

The line

For Each Word In myDoc

doesn't do anything like what you apparently think it does. VBA doesn't
understand it at all.

You need to know that a Document object has properties, one of those
properties is the collection of words in the document, and the way to refer
to that collection is "myDoc.Words". The dot in the middle is actually an
operator, meaning "get the property whose name follows the dot". You also
need to know (by reading the VBA Help for the Words collection) that the
members of the collection are Range objects. That means the loop variable
has to be a Range object, which you have to declare in a Dim statement --
and you should avoid naming it "Word" or any other name that's used in the
object model. So you might get somewhere with code like

Dim myDoc As Document
Dim myWord As Range

Set myDoc = ActiveDocument

For Each myWord In myDoc.Words
....
Next myWord

But wait, we're not clear yet! The next problem is that you never assign a
value to the variable myPre, so the InStr function has no idea what to look
for. As it happens, when you tell InStr to look in a string for occurrences
of an empty Variant (which is the datatype of myPre, because you never
declared that variable either), it always returns the value 1, so the
statement inside the If...Then executes every time. Fortunately, if "xx_"
doesn't occur in myWord (actually, in myWord.Text, which is the default
property of the Range object), the Replace function does nothing. Still,
assigning it to myWord (again, actually myWord.Text) causes the loop
variable to changed, so the loop keeps repeating the same range in an
endless loop.

Declaring myPre as a String and assigning the value "xx_" to it stops the
endless loop, but instead the If condition is always false. The problem now
is that VBA does not consider something like "xx_abcd" to be one word, but
three: "xx" is a word that will be returned as myWord.Text, "_" is the
second word, and "abcd" is the third. None of them contain the substring
"xx_". That is, you'll never find what you're looking for with this kind of
code!

But wait! There is a much simpler and easier answer. Just do a Replace
operation to replace "xx_" with "yy_". You can do it without a macro, in the
Edit > Replace dialog. Or you can do it in a macro with code like this:

Sub ReplaceWord()

Dim myDoc As Document
Dim myRange As Range

Set myDoc = ActiveDocument
Set myRange = myDoc.Content

With myRange.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "xx_"
.Replacement.Text = "yy_"
.Format = False
.Forward = True
.Wrap = wdFindStop
.Execute Replace:=wdReplaceAll
End With

End Sub


--
Regards,
Jay Freedman
Microsoft Word MVP
Email cannot be acknowledged; please post all follow-ups to the newsgroup so
all may benefit.
 
D

Daniel Bonallack

Thank you very much.

I didn't explain very well to start off, but the simple find and replace
won't do. You see, once I find the word that begins with "xx_", I then have
to do several things to it (for example, just in these "qualifying" words,
replace the "a" with the number 1, the "b" with a number 2 - but I didn't
want to bore you with the details).

So your code will surely help, thank you.

But one question:
Will your code do the replacement just on one word at a time, or will it do
it throughout the whole document?
 
G

Gordon Bentley-Mix

You've the patience of a saint Jay... ;-P
--
Cheers!
Gordon

Uninvited email contact will be marked as SPAM and ignored. Please post all
follow-ups to the newsgroup.
 
J

Jay Freedman

Since you want to do several things with the found text, you should use just the
"find" part and not the "replace"; once having located the right place, do the
work; and then find again, in a loop, until there are no more occurrences.

Since you want the Find to locate the entire "word" starting with "xx_" and
continuing until you reach a space or punctuation, you should use a wildcard
search (http://www.gmayor.com/replace_using_wildcards.htm) for the pattern
"xx_[A-Za-z0-9]@>"

The general outline of the macro is this:

Sub demo()
Dim myDoc As Document
Dim myRg As Range
Dim myText As String

Set myDoc = ActiveDocument
Set myRg = myDoc.Content

With myRg.Find
.Text = "xx_[A-Za-z0-9]@>"
.MatchWildcards = True
.Format = False
.Forward = True
.Wrap = wdFindStop

While .Execute
' At this point, myRg has been
' redefined to cover the found
' text. You can modify it...
myText = myRg.Text
myText = Replace(myText, "xx_", "yy_")
myText = Replace(myText, "a", "1")
myText = Replace(myText, "b", "2")
' etc.
' and then put it back into the document
myRg.Text = myText

' prepare for next loop
myRg.Collapse wdCollapseEnd
Wend
End With
End Sub

The .Execute method returns the value True when it successfully matches the
pattern, or False if there are no more occurrences in the rest of the document.
So the While...Wend loop executes repeatedly until there are no more words that
start with "xx_".

As the comment says, each time .Execute finds a match, the range is redefined to
cover just the found text. Then you can modify the range's contents. The last
part is to move the range to a point after the modified text so the next
..Execute starts at the end of the last match and continues toward the end of the
document.

Besides the fact that this method works (unlike the attempt to test each "word"
in the document for the prefix), it's also _much_ faster than explicitly looping
through all the words in the document.

--
Regards,
Jay Freedman
Microsoft Word MVP
Email cannot be acknowledged; please post all follow-ups to the newsgroup so all
may benefit.
 
J

Jay Freedman

You've the patience of a saint Jay... ;-P

Not at all... (and don't catch me on a bad day <g>).

Questions like this one serve to point out how truly unintuitive the VBA syntax
and the Office object model are. A detailed explanation of why the attempt
doesn't work might serve some people as a better tutorial than explaining the
"right" way from the beginning.

Really, don't you think that VBA _ought_ to be able to understand this
plain-English statement?

For Each Word In ActiveDocument
' do something with the word
Next Word

And since you can (once you get the syntax right) get a member of the
ActiveDocument.Words collection, don't you think Word should be smart enough to
consider "xx_abcd" to be one word and not three? Why would each punctuation mark
be a "word"?

There are very few ways to find out this information other than asking someone
who already knows -- the Help is hard to use and getting worse. And I've always
found lessons learned from fixed mistakes to be easier to remember than things I
just read about.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top