I am attempting to write a macro that will automatically verify that the forms present in two documents are the same.
To do this I need to search through one document and create an array that is a list of all of the forms in that document. Each form is designated by a unique code like AB001 or E363. I am currently searching for these terms and highlighting them using this code I blatantly stole off the internet.
Dim word As Range
Dim wordcollection(9) As String
Dim words As Variant
'Define list.
'If you add or delete, change value above in Dim statement.
wordcollection(0) = "PJ"
wordcollection(1) = "E1233"
wordcollection(2) = "E048"
wordcollection(3) = "E144"
wordcollection(4) = "E849"
wordcollection(5) = "E977"
wordcollection(6) = "IL0021"
wordcollection(7) = "MISC001"
wordcollection(8) = "CG0001"
wordcollection(9) = "CG2107"
'Set highlight color.
Options.DefaultHighlightColorIndex = wdYellow
'Clear existing formatting and settings in Find feature.
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
'Set highlight to replace setting.
Selection.Find.Replacement.Highlight = True
'Cycle through document and find words in collection.
'Highlight words when found.
For Each word In ActiveDocument.words
For Each words In wordcollection
With Selection.Find
.Text = words
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
Next
Next
End Sub
I need to figure out how to copy the values that are present in the document into a new array
Then I need to search another document for the same values and copy the values that are present in that document into another new array
Finally I need to compare both of the new arrays and print a list of the values that are present in new array A but not new array b and vice versa.
Any help would be appreciated. The extent of my VBA experience is writing macros to automatically copy data in formfields to new formfields so even a basic understanding of how to do this would be appreciated.
Thanks!
Doing a ReplaceAll in your existing code doesn't give you any runtime feedback. You have to isolate the Found variable and then you can determine what to do. My recommendation is to separate the single macro into two parts with the second macro providing the feedback that the code was found so that you can then take action.
To provide you with an example of what I am attempting to describe, below is an example and the output are separate text files that show what codes exist in each document. Hopefully you can adapt this to meet your requirements.
Sub FindCodes()
Dim doc As word.Document
Dim i As Long, wrkFolder As String, fName As String
Dim oFile As String, FileNum As Integer
Dim Codes(0 To 2) As String
Codes(0) = "PJ"
Codes(1) = "E1233"
Codes(2) = "E048"
On Error GoTo errHandler
wrkFolder = "c:\users\<your id>\documents\test\"
fName = Dir(wrkFolder & "*.docx", vbNormal)
Do While fName <> vbNullString
Set doc = Documents.Open(wrkFolder & fName)
oFile = Left(doc.FullName, InStrRev(doc.FullName, ".") - 1) & "_Codes.txt"
On Error Resume Next
Kill oFile
On Error GoTo errHandler
FileNum = FreeFile()
Open oFile For Append As #FileNum
Print #FileNum, doc.Name
For i = 0 To UBound(Codes)
If Not CheckDocument(doc, Codes(i)) = vbNullString Then
'the code was found in the document
'print it in a text file
Print #FileNum, Codes(i)
End If
Next
Close #FileNum
doc.Save
doc.Close
fName = Dir()
Loop
errHandler:
If Err.Number <> 0 Then
MsgBox Err.Description, vbExclamation, "Find Codes"
Err.Clear
End If
End Sub
Private Function CheckDocument(ByRef doc As word.Document, StrCode As String) As String
Dim rng As word.Range
For Each rng In doc.StoryRanges
'will search headers, footers and the document body
With rng.Find
.ClearFormatting
.Format = True
.Forward = True
.MatchCase = True
.MatchWholeWord = True
.Text = StrCode
.Wrap = wdFindStop
.Execute
If .Found Then
'this will highlight the first code found and then exit
'does it really need to highlight all places the code
'was found? If so, comment out this IF statement and
'use the loop method instead
rng.HighlightColorIndex = wdYellow
CheckDocument = .Text
Exit Function
End If
' Do While .Found
' rng.HighlightColorIndex = wdYellow
' CheckDocument = .Text
' Loop
End With
Next
End Function
Related
I'm struggling to find a way to loop through every word in a Word document and make the word bold if it's in a list of predefined words/terms. The predefined list is in strCollection.
Sub BoldWords()
Dim strCollection(2) As String
strCollection(0) = "test"
strCollection(1) = "john"
strCollection(2) = "later"
For Each strWord In ActiveDocument.Words
'If the strWord is in the strCollection
'strWord.Font.BOLD = True
'End If
Next strWord
End Sub
I can loop through the words okay, but I can't seem to figure out how to do the conditional logic to check if the word is in an array. I don't do a lot of VBA so I appreciate any help here.
I've looked at other answers to this question like this one but they don't run at all. Maybe they're for older versions of Word? I'm on O365.
Looping through every Word in a document is terribly inefficient. You should instead consider using Find/Replace. For example:
Sub BoldWords()
Application.ScreenUpdating = False
Dim ArrFnd As Variant, i As Long
'Array of Find expressions
ArrFnd = Array("test", "john", "later")
With ActiveDocument.Range.Find
.ClearFormatting
.Replacement.ClearFormatting
.Format = False
.Forward = True
.MatchCase = False
.MatchWholeWord = True
.Replacement.Text = "^&"
.Replacement.Font.Bold = True
'Process each item from ArrFnd
For i = 0 To UBound(ArrFnd)
.Text = ArrFnd(i)
.Execute Replace:=wdReplaceAll
Next
End With
Application.ScreenUpdating = True
End Sub
You will need a nested loop to compare the word with each key in the collection, and then do stuff with it. It also might be a matter of putting this code in the Document_Open() event handler so that it runs when the document is opened.
Private Sub Document_Open()
Dim colCollection : Set colCollection = CreateObject("Scripting.Dictionary")
Dim strWord, Key
colCollection.Add 0, "test"
colCollection.Add 1, "john"
colCollection.Add 2, "later"
For Each strWord In ActiveDocument.Words
For Each Key in colCollection.Keys
If strWord = colCollection.Item(Key) Then
strWord.Font.Bold = True
End If
Next
Next
End Sub
Also see: How to create collection object in vbscript?
Also see: https://learn.microsoft.com/en-us/office/vba/api/word.document.open
I have an extract of code that searches for a word in a document from the current selection to the end. The intention of this is so the next time it's run it will find the next instance and so on.
It works fine until it finds a word within a table, at which point it won't find anything after that entry. I need to be able to find words in tables as well as text. It also runs as a function in a userform (running modeless), waiting for user input then providing different words, looping and performing actions depending on user input. So I don't believe I can run my other code within the find section (although I'm happy to be corrected).
Sub test1()
Dim list() As String
Dim wrd As String
Dim mrk As Integer
wrd = "ABC" 'Get next word from list
'set range to search as from current selection (previously found) to end of document
Dim DocRng
Set DocRng = ActiveDocument.Range(Start:=Selection.End, End:=ActiveDocument.Content.End)
mrk = Selection.End 'Mark end of previously found instance (current selection)
With DocRng.Find 'Find next instance of word and select it
.Text = wrd
.MatchCase = True
.Forward = True
.Execute
DocRng.Select
End With
If Selection.End = mrk Then 'If selection hasn't changed inform user and go to start of document
MsgBox ("Reached end of document.")
Selection.GoTo What:=wdGoToLine, Which:=wdGoToAbsolute, Count:=0
End If
tmp = Selection.Text 'Save currently selected text
End Sub
How do I get it to find entries past the table?
You can run other code within a Find/Replace loop, using code like:
Sub Demo()
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = InputBox("What is the Text to Find")
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = True
.MatchWholeWord = True
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Execute
End With
Do While .Find.Found
.Select
Select Case MsgBox("Replace this one?", vbYesNoCancel)
Case vbCancel: Exit Sub
Case vbYes: .Text = InputBox("Replacement text")
Case Else
End Select
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
End Sub
Such code is unaffected by tables.
By searching the whole document (or specified range) and storing the locations of each instance in an array, you can then compare those locations to the current selection and select the instance after the current selection.
Function search()
Dim list() As String
Dim Wrd As String
Dim k As Integer
Dim Nfound As Boolean
Dim Def As String
Dim location() As String
'Search document and get locations of each instance of a word
Wrd = "ABC" 'Get next word from list
Def = "Alphabet"
k = 1
Dim DocRng
Set DocRng = ActiveDocument.Content 'search whole document
With DocRng.find
.Text = Wrd
.MatchCase = True
Do While .Execute 'For each entry found, store start and end to array
ReDim Preserve location(2, k)
location(1, k) = DocRng.Start
location(2, k) = DocRng.End
k = k + 1
Loop
End With
'Compare the found locations against the current selection and select the first instance after current selection
Nfound = True 'Set as not found until it is found
j = Selection.End 'mark current cursor location
For k = 1 To UBound(location, 2)
If location(1, k) > j + Len(Def) Then '+ Len(Def) accounts for changes to text
ActiveDocument.Range(Start:=location(1, k), End:=location(2, k)).Select
Nfound = False
Exit For
End If
Next
If Nfound Then 'if not found got to first instance found
k = 1
ActiveDocument.Range(Start:=location(1, k), End:=location(2, k)).Select
End If
End Function
I happen to have problems trying to manipulate the below code to my liking.
First off, the code below deletes everything in between the start and end conditions I have stipulated in my program.
I would like to change this, to delete everything besides those stipulated between the start and end words.
Sub SomeSub()
Dim StartWord As String, EndWord As String
Dim Find1stRange As Range, FindEndRange As Range
Dim DelRange As Range, DelStartRange As Range, DelEndRange As Range
Application.ScreenUpdating = False
Application.DisplayAlerts = False
'Setting up the Ranges
Set Find1stRange = ActiveDocument.Range
Set FindEndRange = ActiveDocument.Range
Set DelRange = ActiveDocument.Range
'Set your Start and End Find words here to cleanup the script
StartWord = "From: Research.TA#traditionanalytics.com|Tradition Analytics Commentary| | |"
EndWord = "This message has been scanned for malware by Websense. www.websense.com"
'Starting the Find First Word
With Find1stRange.Find
.Text = StartWord
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindAsk
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
'Execute the Find
Do While .Execute
'If Found then do extra script
If .Found = True Then
'Setting the Found range to the DelStartRange
Set DelStartRange = Find1stRange
'Having these Selections during testing is benificial to test your script
DelStartRange.Select
'Setting the FindEndRange up for the remainder of the document form the end of the StartWord
FindEndRange.Start = DelStartRange.End
FindEndRange.End = ActiveDocument.Content.End
'Having these Selections during testing is benificial to test your script
FindEndRange.Select
'Setting the Find to look for the End Word
With FindEndRange.Find
.Text = EndWord
.Execute
'If Found then do extra script
If .Found = True Then
'Setting the Found range to the DelEndRange
Set DelEndRange = FindEndRange
'Having these Selections during testing is benificial to test your script
DelEndRange.Select
End If
End With
'Selecting the delete range
DelRange.Start = DelStartRange.Start
DelRange.End = DelEndRange.End
'Having these Selections during testing is benificial to test your script
DelRange.Select
'Remove comment to actually delete
DelRange.Delete
End If 'Ending the If Find1stRange .Found = True
Loop 'Ending the Do While .Execute Loop
End With 'Ending the Find1stRange.Find With Statement
End Sub
Hah! That's a new twist There's certainly more than one way to go about it; my inclination would be to work with (at least) three Ranges. Something like this:
Sub FindAndDeleteEverythingElse()
Dim strFind1 As String, strFind2 As String
Dim rngDoc As word.Range, rngFind1 As word.Range
Dim rngFind2 As word.Range
Dim bFound As Boolean
strFind1 = "You"
strFind2 = "directly."
Set rngDoc = ActiveDocument.content
Set rngFind1 = rngDoc.Duplicate
Set rngFind2 = rngDoc.Duplicate
With rngFind1.Find
.Text = strFind1
bFound = .Execute
End With
If bFound Then
With rngFind2.Find
.Text = strFind2
bFound = .Execute
End With
If bFound Then
rngDoc.End = rngFind1.Start
rngDoc.Delete
rngDoc.Start = rngFind2.End
rngDoc.End = ActiveDocument.content.End
rngDoc.Delete
End If
End If
End Sub
The "main" Range is that of the entire document: ActiveDocument.Content. The Range object is a bit different than other objects, if you set one Range to another it becomes that Range, not a copy. So you need the Duplicate method to make a copy of a Range. This lets you use Find independently for the various Ranges.
If the first Find is successful, the second one is executed. If that is also successful then the Document Range's end-point is set to the starting point of the successful Find and the content of the Range deleted. The Document Range is then re-defined to start at the end-point of the second found Range and end at the end of the Document, and deleted.
You will probably have to set more Range.Find properties than I did in this code snippet - I used the absolute minimum to make working with the Ranges clearer.
There maybe another way but till then you can do this.
try to add dummy character after and before your string like this
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchWildcards = False
.Text = "From: Research.TA#traditionanalytics.com|Tradition Analytics Commentary| | |"
.Replacement.Text = "From: Research.TA#traditionanalytics.com|Tradition Analytics Commentary| | |######"
.Execute Replace:=wdReplaceAll
.Text = "This message has been scanned for malware by Websense. www.websense.com"
.Replacement.Text = "######This message has been scanned for malware by Websense. www.websense.com"
.Execute Replace:=wdReplaceAll
End With
End With
End Sub
Then try to set range between ###### and ######
this is best answer to set range select a range of text from one Word document and copy into another Word document
Please note that in my word 2007 it is not possible to find within hyperlinks. Try to remove all hyperlink or within range before doing replacement.
another best answer for that: How do you remove hyperlinks from a Microsoft Word document?
I have 2 headers or markers that are a part of my RTF document. In my example I am showing a sentence when in reality it will be multiple sentences or paragraphs. I have used brackets instead of less than and greater than signs as they disappear in my question. All I want to do is replace the text between the 2 markers with the following sentence, "text goes here", without quotation marks.
[EmbeddedReport]Lots of text, thousands of character, multiple paragraphs[/EmbeddedReport]
I want replace all the text between the 2 markers replaced with "text goes here".
It would end up looking like this...
"[EmbeddedReport]text goes here[/EmbeddedReport]"
I've literally spent 2 days trying to solve this. Any help would be appreciated.
This is the last thing I tried...
Sub RemoveReport()
Dim c As Range
Dim StartWord As String, EndWord As String
Selection.HomeKey Unit:=wdStory
StartWord = "<ImageTable>"
EndWord = "</ImageTable>"
Set c = ActiveDocument.Content
c.Find.ClearFormatting
c.Find.Replacement.ClearFormatting
With c.Find
.Text = StartWord & "*" & EndWord
' MsgBox (.Text)
.Replacement.Text = "<ImageTable>text goes here</ImageTable>"
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = True
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
c.Find.Execute
While c.Find.Found
Debug.Print c.Text
'~~> I am assuming that the start word and the end word will only
'~~> be in the start and end respectively and not in the middle
Debug.Print Replace(Replace(c.Text, StartWord, ""), EndWord, "")
c.Find.Execute
Wend
End Sub
Word VBA is not my area of expertise, but it seems similar to a question I answered a few days ago.
Turns out the wildcard match was not doing what I hoped it would do, or at least it was not reliable. Also, I ran in to some trouble using angle brackets, so this uses square brackets. I suspect that word treats the angle brackets as markup/syntax, and thus does not interpret them as text in the Find object. There is probably a way around this, but Word VBA is not my specialty. There is also probably a more elegant solution, but again, Word VBA is not my specialty :)
Try something like this:
Option Explicit
Sub Test()
Dim doc As Document
Dim txtRange As Range
Dim startTag As String
Dim endTag As String
Dim s As Long
Dim e As Long
startTag = "[EmbeddedReport]"
endTag = "[/EmbeddedReport]"
Set doc = ActiveDocument
Set txtRange = doc.Content
'Find the opening tag
With txtRange.Find
.Text = startTag
.Forward = True
.Execute
If .Found Then
s = txtRange.Start
Else
GoTo EarlyExit
End If
End With
'Find the closing tag
Set txtRange = doc.Range(txtRange.End, doc.Content.End)
With txtRange.Find
.Text = endTag
.Forward = True
.Execute
If .Found Then
e = txtRange.End
Else
GoTo EarlyExit
End If
End With
Set txtRange = doc.Range(s, e)
txtRange.Text = startTag & "text goes here" & endTag
Exit Sub
EarlyExit:
MsgBox "Header not found in this document!", vbInformation
End Sub
It takes some time to figure it out at first, but learning to navigate the object model reference documentation for VBA will make these tasks a lot easier to figure out in the future.
I am in the process of putting together a Word macro (below) that parses a table of acronyms in one Word document and highlights every occurrence of these acronyms in another Word document. This appears to be functional.
However, I would like to also have the macro differentiate acronyms that are in parentheses from those that are not. For example,
The soldier is considered Away Without Leave (AWOL). AWOL personnel are subject to arrest.
It seems as though the range "oRange" that defines the found acronym could be evaluated, if it is first expanded in the Do-While loop using this code:
oRange.SetRange Start:=oRange.Start - 1, End:=oRange.End + 1
However, none of my attempts to code a solution seem to work (they put the macro into an infinite loop or result in error messages). I'm fairly new to VBA programming and am obviously missing something regarding how the loops are operating.
My question is: is there a way to duplicate the range "oRange" for subsequent manipulation or is there some other method that I should be using?
Thanks for any assistance you can provide!
Sub HighlightAcronyms()
Dim wdDoc As Object
Dim wdFileName As Variant
Dim TableNo As Integer 'table number in Word
Dim oRow As Row
Dim oCell As Cell
Dim sCellText As String
Dim oDoc_Source As Document
Dim strListSep As String
Dim oRange As Range
Dim n As Long
Dim sCellExpanded As String
'Application.ScreenUpdating = False
strListSep = Application.International(wdListSeparator)
'*** Select acronym file and check that it contains one table
wdFileName = WordApplicationGetOpenFileName("*.docx", True, True)
If wdFileName = False Then Exit Sub '(user cancelled import file browser)
Set wdDoc = GetObject(wdFileName) 'open Word file
With wdDoc
TableNo = wdDoc.Tables.Count
If TableNo = 0 Then
MsgBox "The file """ & wdFileName & """ contains no tables.", _
vbExclamation, "Import Word Table"
ElseIf TableNo > 1 Then
MsgBox "The file """ & wdFileName & """ contains multiple tables.", _
vbExclamation, "Import Word Table"
End If
End With
'*** steps through acronym column
wdDoc.Tables(1).Cell(1, 1).Select
Selection.SelectColumn
For Each oCell In Selection.Cells
' Remove table cell markers from the text.
sCellText = Left$(oCell.Range, Len(oCell.Range) - 2)
sCellExpanded = "(" & sCellText & ")"
n = 1
'need to find foolproof method to select document for highlighting
Documents(2).Activate
Set oDoc_Source = ActiveDocument
With oDoc_Source
Set oRange = .Range
With oRange.Find
.Text = sCellText
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = True
.MatchWildcards = False
Do While .Execute
If n = 1 Then
oRange.HighlightColorIndex = wdGreen
Else
oRange.HighlightColorIndex = wdYellow
End If
'trying to add code here to expand oRange and compare it to sCellExpanded
n = n + 1
Loop
End With
End With
Next oCell
Set wdDoc = Nothing
End Sub
Try This
Define two ranges instead of merging the oRange.
See this sample code (TRIED AND TESTED)
Sub Sample()
Dim strSearch As String, sCellExpanded As String
Dim oRange As Range, newRange As Range
strSearch = "AWOL"
sCellExpanded = "(" & strSearch & ")"
Set oRange = ActiveDocument.Range
With oRange.Find
.ClearFormatting
.Text = strSearch
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
Do While .Execute
If n = 1 Then
oRange.HighlightColorIndex = wdGreen
Else
oRange.HighlightColorIndex = wdYellow
End If
'~~> To check if the found word is not the 1st word.
If oRange.Start <> 0 Then
Set newRange = ActiveDocument.Range(Start:=oRange.Start - 1, End:=oRange.End + 1)
If newRange.Text = sCellExpanded Then
'
'~~> Your code here
'
newRange.Underline = wdUnderlineDouble
End If
End If
n = n + 1
Loop
End With
End Sub
SNAPSHOT
Unable to upload image at the moment. imgur server is down at the moment.
You may see this link
http://wikisend.com/download/141816/untitled.png