VBA Compare positions of two words in a Word document - vba

I'm writing a module for defining acronyms in Word documents. The script gets the acronym and definition from an Excel document. What I'm having trouble with is comparing the location of the first instance of the acronym with the location of the first instance of the full definition.
Ultimately, I need to make sure the first instance of the acronym occurs immediately after the first instance of the definition, enclosed in parentheses. After this is done, the script will need to remove subsequent instances of the definition, so I also need to figure out how to remove all but the first instance of a definition.
The end result should look something like this:
....This document is about software as a service (SaaS). SaaS is software that is hosted by someone else. Rather than installing it on your own computer, you access it through a Web browser. There are many types of SaaS.
....
How can I get the positions of these two elements and or compare their positions?
In the example above, how would I find the first instance of "SaaS" and make sure it occurred exactly two positions after (space, open parentheses) the definition (assuming the definition actually appears in the document)?
'Selects first instance of acronym. Get start and end positions of first instance of acronym.
Selection.HomeKey Unit:=wdStory
Selection.Find.Execute Acronym 'Acronym is a variable. Now that it's selected, I need to get it's start position (or the position of the cursor if the cursor is at the start of the acronym) or find a way to compare it's position to the UserSelection variable.
'Is the definition in the document?
'If no, add definition before first instance of acronym.
'If yes, get start and end positions of first instance of definition.
'Is end position of first instance of definition adjacent to start position of first instance of acronym? If not, which is first?
'If definition is first, add acronym behind definition.
'If acronym is first, add definition in front of acronym and delete remaining instances of definition.
'Highlights all instances of the acronym in green
With ActiveDocument.Content.Find
.MatchCase = True
.MatchWholeWord = False
With .Replacement
.Highlight = True
End With
.Execute Replace:=wdReplaceAll, Wrap:=wdFindContinue, FindText:=Acronym, ReplaceWith:=Acronym
End With
Any help or insight would be appreciated, as I'm completely at a loss and having no luck with Google.
-Vince

I think the following code snippet can help you:
Sub example(acronym, definition)
Selection.Find.ClearFormatting
With Selection.Find
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchAllWordForms = False
End With
ActiveDocument.Range(0, 0).Select ' go to beginning of document
Selection.Find.Text = acronym
Selection.MatchSoundsLike = False
If (Not Selection.Find.Execute) Then
' acronym not found in this document
Exit Sub
End If
ActiveDocument.Range(0, 0).Select ' go to beginning of document
Selection.Find.Text = definition
Selection.MatchCase = False
Selection.MatchSoundsLike = True
While (Selection.Find.Execute)
'
Selection.Delete ' delete all definitions
'
Wend
ActiveDocument.Range(0, 0).Select ' go to beginning of document
Selection.Find.Text = acronym
Selection.MatchSoundsLike = False
If (Selection.Find.Execute) Then
Selection.InsertBefore "(" & definition & ")"
End If
End Sub
Note: I also found that authors make mistakes in the definitions (minor variations) and even an extra unintended space messes up the find.

Related

vba ".find" stopped working

Currently this code runs with no error message but does not make the requested replacement:
Private Sub TestingButton_Click()
Dim RngFound As Range
Dim FileToProcess As Word.Document
Dim WordInstance As Object
Set WordInstance = CreateObject("Word.Application") 'For these tests I close Word first.
Set FileToProcess = WordInstance.Documents.Open("c:\sarah\junk\Attach.doc")
WordInstance.ActiveDocument.Range.Select 'Gets the whole document
Toolbox.SetupFind (WordInstance.ActiveDocument.Range)
With WordInstance.ActiveDocument.Range.Find
.Text = "rock"
.Replacement.Text = "found it!"
.Execute
End With
End Sub
In the Toolbox module:
Public Function SetupFind(ByRef RngPassed As Word.Range)
With RngPassed.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ""
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Execute
End With
End Function
The code above is from a test database that I created for troubleshooting. My destination is an Access form whose purpose is to extract some information from a set of documents, and place the information in a database. It was working fine. It stopped working after a particularly spectacular crash. I tried to remove a label while the form was in break mode. (I know,... deep regret) I couldn't close Access even with ctrl-break. Probably the wierdest part is that similar code now no longer works in back-up copies of the database either.
Here are things I've tried that haven't worked:
Re-import all objects into a new database.Similarly, rebuild the backend database. Break the form's code (a couple thousand lines)
into modules. Copy all the code into Notepad, save it, then create a
button in a new empty database. Recreate subs & functions by
typing, then once they exist, paste in the code from Wordpad. Reset
the form's references, which include MSWord. Make a brand new form
in the new database with one button that has only the displayed
code. Use a defined range: This all started when I got a persistent
error in RngToSearch.find.execute findtext:="reason". The error
highlighted .find and said 'Argument not optional.' So among other
things, I switched to a selection rather than a defined range in my
attempts to isolate the problem. But working with a range rather
than a selection is where I really need to end up again.
Possibly relevant observations:
Other forms in the same database with lots of backend code work fine. So do other routines in the same form.
Only one document is open. As far as I can tell, there's nothing unusual about the document. I have tried multiple documents.
The text to find exists in the document, outside of a table.
Things that have worked, as they might be clues:
In Word straight up, no code, no nothing, use 'find' to select the targeted word.
WordInstance.ActiveDocument.Range.Text = Replace(WordInstance.ActiveDocument.Range.Text, "rock", "Found it!"). Trouble is, what I ultimately need to do again is way more complex than Replace can handle.
Specify the scope for the replacement(s). The code worked for me from Access 2010 with this change in TestingButton_Click() ...
'.Execute
.Execute Replace:=wdReplaceAll
Toolbox.SetupFind also calls .Execute. Since the find and replacement text are both empty strings at that point and no scope is specified, .Execute doesn't cause harm ... but it doesn't seem useful either.

Word 2010, Move cursor to specific text, replace the text with file content

I have a line of text that says "[EmbeddedReport]report goes here[/EmbeddedReport]".
I want to replace the "report goes here" with an empty string and put the cursor right after the [EmbeddedReport] marker. I will then run the following code...
With Selection.InsertFile ('c:\Temp\Report.rtf')
That should put the text of the report between the markers. I tried to locate the cursor with the following code. It does not seem to work.
.Selection.Find
.ClearFormatting
.MatchWholeWord = False
.MatchCase = False
.Execute FindText:="report goes here"
Only problem is the cursor is not between the [EmbeddedReport] and [/EmbeddedReport] and the file is inserted wherever the cursor was located before the macro is run.
you will possibly need to call Find object twice:
FIRST- to find the whole phrase [EmbeddedReport]report goes here[/EmbeddedReport],
SECOND- to find a report goes here text within result of step first. Important- you don't need to replace that phrase- it will be selected and replaced with the text you import using Selection.InsertFile method.
Here is proposed code (tested for sample file):
'FIRST- find [EmbeddedReport]report goes here[/EmbeddedReport]
With Selection.Find
.Text = "(\[EmbeddedReport\])text goes here(\[\/EmbeddedReport\])"
.Forward = True
.Wrap = wdFindContinue
.MatchWildcards = True
End With
'...and select it
Selection.Find.Execute
'SECOND- find only text to replace 'text goes here'
With Selection.Find
.Text = "text goes here"
.Replacement.Text = " "
.Forward = True
.Wrap = wdFindContinue
.MatchWildcards = True
End With
'end select it
Selection.Find.Execute
'now you could insert your file
Selection.InsertFile "c:\Temp\Report.rtf"
The usual way to fix these kind of problems is to record a macro - and perform the tasks you need to move the cursor and highlight text, etc. - and then examine the code that the macro wrote to perform those tasks. This will usually give you the insight to tailor your own code to perform the same tasks.

deleting certain lines in ms word 2007

I would like to delete certain lines from my word document using a VBA macro. Basically the (block of) text to be deleted (and replaced by "***") follows a certain pattern (below).
Bottom of Form
perma-link
Top of Form
save
Bottom of Form
[+] ....
[–] ....
Top of Form
"...." represents text that changes every block, but for sure the line starts with "[+]" or "[-]".
Please suggest a suitable macro
EDIT: In the screenshot, I would like to keep the text in yellow and delete the rest. (in the actual file, the text isn't in yellow)
PS-FYI, I tried using the example looping a find and delete row macro (for line by line deletion) but i get a runtime error 5941 with debugging option highlighting the line "selection.row.delete" in the macro.
What does this mean?
Assuming that the example list is a list of paragraphs beginnings the following code should do the trick. What you have to do is to place all 'paragraphs starting' into array arrRemove as I did for the test. If any of the mark is a special marks (see this link for additional information) you need to add \ in front of it as I did for [+] and [-]. Hope this is what you are looking for.
Sub Macro2()
Dim arrRemove As Variant
arrRemove = Array("Bottom of Form", "perma -link", "Top of Form", _
"\[+\]", "\[\-\]", "Donec", "In")
Dim i!
For i = 0 To UBound(arrRemove)
Activedocument.Range(0,0).select
Selection.Find.ClearFormatting
With Selection.Find
.Text = arrRemove(i) & "*^13"
.Replacement.Text = "" 'replace with nothing
.Forward = True
.Wrap = wdFindContinue
.MatchCase = False
.MatchWildcards = True
End With
Selection.Find.Execute Replace:=wdReplaceAll
Next i
End Sub
The above macro will remove all yellow paragraph in the following document.

VBA code to search for text and stop at next occurrence including footnotes, headers etc

I am trying to write some code that will search through all the stories including headers, footers, footnotes etc and then stop at each occurrence so the user can make a decision about it (it may or may not change), then click a button again to move to the next occurrence (like Word's Find Next).
I am aware there is some pretty tricky code for performing a search and replace using the range object and I have that code working for another part of this project, but what I can't do is make it search and stop at the selected text, then carry on looking in the different stories, it just stops at the end of the main document.
The code below looks as though it should work but even if the footnote for example has the text to be searched for, it is ignoring it. I have done a thorough search of this site and others and have found several examples for search and replace, but none for search and stop/select.
Any advice gratefully received - thank you.
Sub TestSelection()
Dim rngStory As Range
Dim docDocument As Document
Set docDocument = ActiveDocument
With docDocument
For Each rngStory In .StoryRanges
Select Case rngStory.StoryType
Case 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
Debug.Print rngStory.StoryType
With Selection.Find
.ClearFormatting
.Text = "XYZ"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute
If Selection.Find.Found = True Then
Exit Sub
End If
End Select
Next rngStory
End With
End Sub
Whether this is your problem in this case I don't know, but while your loop iterates over all the ranges returned by StoryRanges, it does not process the entire document. It only includes the first part of each story. (So, for example, if there are several sections in your document, it will only include the header & footer from the first section).
You need to use the NextStoryRange method in order to access the entire story. Look that up in VBA help for an example loop construct. (It's a horrible API - just as bad as Range.Find!).
Also, be aware that executing a search will change the selection, so Selection.Find will suddenly be searching in the last result, rather than the entire range.

How Do I Find All Acronyms in an MS Word Document Using a Macro?

I have a document with many acronyms that need to be captured and put into an acronyms table at the end of the document.
The term acronym has various meanings. I'd like to create a table that has all of the words that are initialized; two or more capitalized letters that are short for a longer meaning. I.e., CD-ROM, USB, SYNC, MMR, ASCAP, etc.
How do I create a macro to do this?
Something like this might get you started. Add a reference to "Microsoft VBScript Regular Expressions" (Edit Macro: Tools > References). This library is the file, "vbscript.dll".
You may need to adjust the regexp if all your acronyms aren't only upper-case letters (eg some may contain numbers).
Sub Acronyms()
Dim dict, k, tmp
Dim regEx, Match, Matches
Dim rngRange As Range
Set regEx = New RegExp
Set dict = CreateObject("scripting.dictionary")
regEx.Pattern = "[A-Z]{2,}" '2 or more upper-case letters
regEx.IgnoreCase = False
regEx.Global = True
Set Matches = regEx.Execute(ActiveDocument.Range.Text)
For Each Match In Matches
tmp = Match.Value
If Not dict.Exists(tmp) Then dict.Add tmp, 0
dict(tmp) = dict(tmp) + 1
Next
For Each k In dict.Keys
Debug.Print k, dict(k)
Next k
End Sub
Thanks Tim, your code works great!
If it will be of any use to others, the pattern [A-Z]{1,}([a-z]*|\&|\.*)[A-Z]{1,} will find more acronyms...
(I do not have permission to post comments, hence adding this as answer)
Edit (still no way to add comments): \b[A-Z]{1,}([a-z*]|\&|\.|\-)[A-Z]{1,}\b is more robust, but will fail if the last character of the acronym is not capitalized.
I have found the following works well (where some business name acronyms are tolerable). I use this to test data entries in Access, it should also work for a Word document range.
objRegExp.Pattern = "([A-Z]{1,}((\&(?![A-Z]\s[\w]{3})\w*)+|\.\w*)+)|[A-Z]{2,}(?![A-Z]*\s[A-Z]{1}[a-z])"
J&K =Match
JK&S =Match
J.S.S =Match
JK&S.K =Match
JSK =Match
JK =Match
DKD And Sons =No Match
J&K Engineering =No Match
PKF Rogers and Associates =No Match
I use RegExHero to test my expressions
I used the following to find abbreviations in my PhD thesis. They were all in "()".
regEx.Pattern = "\([A-Z]{1,}([a-z]*|\&|\.|\-*)[A-Z]{1,}\)"
You will be running a macro on the main Word document. Open a separate Word document that is blank. This will be used to store discovered the acronyms.
Press "Record Macro". Choose a unique name, and assign a shortcut key such
as CTRL + ALT + A.
Open the Find dialogue (CTRL + F). Paste the following search text:
<[A-Z]{2,}>. In the Find dialogue, choose "More" > check the box for "Use Wildcards". Click the Find Next button.
Right-click on the selected text, being careful not to change the
highlight. Select Copy from the context menu.
Navigate to the separate Word document (ALT + TAB, select the Word
document). Paste the copied text, and hit Enter. ALT + TAB back to
the original Word document.
Close the find dialogue and click the right arrow once. This moves
the cursor off the highlighted text, and readies it for the next
search.
Stop the macro recording.
You now have a macro that finds a word containing two or more capitalized letters, and saves the text to a separate document. In order to search for the remaining acronyms, press CTRL + ALT + A continuously until the end of the document has been reached. Or, edit the macro, and add while a loop.
Here is what the macro looks like (without the loop):
Sub GetAcronyms()
Selection.Find.ClearFormatting
With Selection.Find
.Text = "<[A-Z]{2,}>"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
End With
Selection.Find.Execute
Selection.Copy
Windows("Document1.docx").Activate
Selection.PasteAndFormat (wdPasteDefault)
Selection.TypeParagraph
Windows("TheOriginalDocument.docx").Activate
Selection.MoveRight Unit:=wdCharacter, Count:=1
End Sub