In a Word 2007 macro that finds text using wildcards, how do I access the group match values?
For example, if I script a macro that searches for DATE: (<*>)^13, how would I find the value of the match group (<*>)?
Thank you,
Ben
Sub Search()
Selection.Find.ClearFormatting
With Selection.Find
.Text = "DATE: (<*>)^13"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
End With
Selection.Find.Execute
End Sub
Good question, +1. Match groups aren't available in Word, but you can kind of hacky get around it. See the last entry on http://www.xtremevbtalk.com/archive/index.php/t-128215.html for a solution.
Doesn't look like Word's find feature can do this. However, if I use VBScript Regular Expressions, my script should be able to get match results via the return value of the vbscript.regexp object's Execute method:
http://windowsdevcenter.com/pub/a/windows/excerpt/wdhks_1/index.html?page=4
Related
I am trying to find specific digits in a Microsoft Word Document which contains text and digits, with VBA.
For example the text in the document is as follows;
(1) 52.203-19, This is a some text here
(2) 52.204-23, Quick brown fox jumped over the lazy dog 52 times.
(3) 52.204-25, I tried to search for a solution 52.204 times.
(4) 52.2, Could not find any luck though
(5) 52.203, this is blowing my mind away with mac 2.36
I wish to find the exact digits "52.2" as a whole.
I don't want to find instances where 52.2 is a part of another number like 52.203 or 52.204.
Also when I would like to find 52.203 then I want to exclude all instances like 52.203-xx where xx could be any two digit number.
In short I would like to find the exact number only as a whole and not in between the numbers, just like Excel's EXACT function.
Should I use RegEx or should I use Word's Advanced Find function with wildcards through VBA?
What I have finds all instances which I don't want.
Selection.Find.ClearFormatting
With Selection.Find
.Text = "52.2"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindAsk
.Format = False
.MatchCase = False
.MatchWholeWord = True
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute
Regular expressions seems like the way to go for this.
First, go to Tools > References in the VBA editor and make sure that there is a check next to the Microsoft VBScript Regular Expressions 5.5 library.
The following code worked for me on your sample text to remove only the '52.2' after the '(4)' without affecting any of the surrounding characters:
Sub removeNumber()
Dim regExp As Object
Set regExp = CreateObject("vbscript.regexp")
With regExp
.Pattern = "\b52.2\b"
.Global = True
Selection.Text = .Replace(Selection.Text, "")
End With
End Sub
\b means word boundary so will not match any digits before or after the '52.5'.
No need for RegEx. You can use Find with wildcards. For explanation see https://wordmvp.com/FAQs/General/UsingWildcards.htm
The solution proposed by Mr. #TimothyRylatt worked for me perfectly specially after the addition of [!-] to avoid the hyphen containing numbers. However, I needed to implement this solution through a VBA Macro so I modified my code a little bit like this.
The working of Modified Code & the Code itself
Sub find_numbers()
Dim Str As String
'Create Search String for WildCard Search
Str = "<" & "52.203" & ">" & "[!-]"
Selection.Find.ClearFormatting
With Selection.Find
.Text = Str
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindAsk
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = True 'make this option true to use WildCards
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute
End Sub
I want a quick way to deidentify documents by removing employees' signatures in comments (yes, I already have one that removes identifying info, just not the text itself). A macro that could search all employee names and replace with "" would be great. I'm sure there is a simple way to do this.
Everything I've tried has failed, either not finding text in comments or not working after I copy/paste the recorded code for Find/Replace one name and adjust for the other 20-30 names. I've tried probably 4 different find/replace codes that work for the main text, but none have worked for the comments.
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "Employee Name 1"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchByte = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "Employee Name 2"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchByte = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
Copying/pasting this through all 20-30 employees fails (no error, just doesn't replace). I'm sure there's a more elegant way to do it anyway, but I'm definitely not familiar enough with code.
It's not 100% clear from the phrasing in the question whether code for removing the comments' author and initials is also required, or just text in the comments that refers to employees' names. So the code below does both.
First, an array is declared and populated with the employees' names that should be removed. This will make it easier to manage the list and shorten the amount of code (doesn't need to be copy/pasted for each employee).
Then, the collection of comments is looped and two tests are perfomed.
Does the comment text contain any employee's name. To determine this, the array of employee names is looped and the presence of the name checked using Instr. If a name is present, Find/Replace is run on the Comment.Range. (Note: this code contains only the barebones Find properties, you may need to add some things if more is needed.)
Remove Authorand Initial information. Here, the list of author names is part of the Case test. If a comment contains one of them, the Author and Initial information is set to an empty string.
Note that it comment's Range does not include the comment's author or initials information, only the content of the comment (which you may know, but others reading this may not).
Sub RemoveAuthorFromAllComments() ' TestLoopCommentWords()
Dim C As Word.Comment, rng As Word.Range
Dim employees() As Variant, e As Variant
employees = Array("Cindy Meister", "John Doe", "Mary Jane")
For Each C In ActiveDocument.Comments
Set rng = C.Range
For Each e In employees
If InStr(rng, e) <> 0 Then
With rng.Find
.Text = e
.Replacement.Text = ""
.Execute Replace:=wdReplaceAll
End With
End If
Next
Select Case C.Author
Case "Cindy Meister", "John Doe", "Mary Jane"
C.Author = ""
C.Initial = ""
Case Else
Debug.Print C.Author
End Select
Next
End Sub
In my new job I'm sending invoices with personalized, dated letters. Each month I go into a word doc for each client and update the date from whatever date we last send an invoice to the date we're sending the current invoice. I know basically nothing about coding, but I have managed to get something together that will search for a month and replace it with a new month. I did this by googling and piecing it together. I have no idea if its good or not, but it works.
Sub Test()
With Selection.Find
.Text = "April"
.Replacement.Text = "May"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
End Sub
My question is, is there a way to search for a date like "Month DD, YYYY" and replace it with a date of my choosing? Also, it won't necessarily be the same previous date.
Thanks in advance
Actually, you don't even need VBA if you don't want to use it --- a wildcard search will do.
Open the Replace dialog, hit More (if necessary), then check Use wildcards.
In the "Find what" box, put MMMM[ ^s][0-9]{1,2},[ ^s][0-9]{4,4}, but with the actual month you want in place of MMMM. It's case-sensitive.
In the "Replace with" box, put the new date.
Hit Replace All.
Bingo! E.g., if MMMM=March, any date in March will be replaced with the new date you specify.
Explanation
[ ^s] is a space or nonbreaking space
[0-9]{1,2} is either one or two ({1,2}) digits ([0-9]).
Similarly, [0-9]{4,4} is exactly four digits.
VBA
For completeness, here it is:
Option Explicit
Option Base 0
Sub Test()
With Selection.Find
.Text = "March[ ^s][0-9]{1,2},[ ^s][0-9]{4,4}" ' ###
.Replacement.Text = "April 1, 2018"
.Forward = True
.Wrap = wdFindContinue
.Format = False
'.MatchCase = False ' ### Not relevant when using wildcards
.MatchWholeWord = False
.MatchWildcards = True ' ###
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
End Sub
PS- Congratulations on starting out in VBA! Your code looks fine to me - it's pretty close to what you'd get from the macro recorder for the same operation, but cleaner. Two tips you don't need just yet :) — always use Option Explicit at the top of every module, and Dim your variables with specific types.
This is my first post, I am not a programmer.
I built a Word macro (visual basic) that does a search and replace for many items (dates and numbers) within a highlighted block of text. Here's a single search and replace segment. It works well, but after each segment a Y/N dialogue box (in this case Word 2003) appears asking if I want to search the remainder of the document--I DONT.
Query: Is there anything I can add to macro that would respond "NO" (after each search and replace segment) as the macro runs so I don't have to select "no" after each of the 20 or so segments?
Typical segment:.
Selection.Find.ClearFormatting.
Selection.Find.Replacement.ClearFormatting.
With Selection.Find.
.Text = "2015".
.Replacement.Text = "2016".
.Forward = True.
.Wrap = wdFindAsk.
.Format = False.
.MatchCase = False.
.MatchWholeWord = False.
.MatchWildcards = False.
.MatchSoundsLike = False.
.MatchAllWordForms = False.
End With.
Selection.Find.Execute Replace:=wdReplaceAll
...
THX
The line
.Wrap = wdFindAsk
tells Word that you want it to ask the user whether to keep searching. Changing that line to
.Wrap = wdFindStop
should fix your problem.
try this:
Application.DisplayAlerts = False
*YOUR CODE HERE*
Application.DisplayAlerts = True
Not sure about Word 2003 (!)
Hi all and thanks in advance for any replies;
This question is about replacing text only under certain conditions.
Background: I'm working on a macro for the editorial department of an academic institution. They get loads of documents that have the same issues and asked for some help to reduce the time they spend on each one.
Two of the things they want:
If a hyphen is between two digits, change it to an en-dash
Change every ampersand (&) to the word "and"
I have a RegExp that finds and replaces those hyphens just fine, but I noticed a problem. My find/replace changes the "display text" of hyperlinks. Same with ampersands. Bad. So what I'm trying to figure out is how to exclude text that has Selection.Style = Word.ActiveDocument.Styles("Hyperlink")
BTW, what's the logical operator for "not equal"? I tried <> and >< but I always get an error telling me that an expression is expected. I'm new to VBA so please forgive the newbie question.
This is working (part of a much larger Sub):
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "([0-9])-([0-9])"
.Replacement.Text = "\1" & Chr$(150) & "\2"
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = True
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
So can I create an If/Then statement to tell it to replace only if the style is not hyperlink?
Thanks again,
Rissa
P.S. I searched for similar posts and found one but it had never been answered.
Maybe a safer way to find out if your selection is a hyperlink is to use the following VBA code:
If Selection.Hyperlinks.Count = 1 Then
MsgBox "The selection is a hyperlink"
Else
MsgBox "The selection is not a hyperlink"
End If
I just tested it quickly and it works perfectly. To answer you second question, operations such as "=" and "<>" are for basic types such as Integer, Float, Long etc. Word.ActiveDocument.Styles("Hyperlink") returns an object. Therefore you would need to use "Is" and "Is Not"
Hope that helps.
(Thanks Black Cr0w, the logical operator is good to know)
OK, here's the deal...Word Macros don't exactly execute linearly.
I eventually figured out how to write an If/Then/Else statement that mostly worked. Mostly. It didn't actually check the condition until after it did a replace (wdReplaceOne). So it would change the first hyphen in a hyperlink and then go "oh, wait! This is a hyperlink!" and then it would skip any subsequent hyphens in that hyperlink.
So I ended up splitting my If/Then/Else into two separate If/Then blocks. The first one says "move along, nothing to do here," and the second one says, "aha! here's where we need a change." The code below, although cringe-worthy, does exactly what I want.
Sub replaceHyphens()
'
' Find hyphens that occur between digits and change them to en-dash, EXCEPT in hyperlinks
'
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "([0-9])-([0-9])"
.Forward = True
.Format = True
.Wrap = wdFindContinue
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = True
.MatchSoundsLike = False
.MatchAllWordForms = False
.Execute
End With
Do While (Selection.Find.Found = True)
If (Selection.Style = ActiveDocument.Styles("Hyperlink")) Then
Selection.Move Unit:=wdSentence, Count:=1
End If
Selection.Find.Execute
If (Selection.Style <> ActiveDocument.Styles("Hyperlink")) Then
Selection.Find.Replacement.Text = "\1" & Chr$(150) & "\2"
Selection.Find.Execute Replace:=wdReplaceOne
End If
Loop
End Sub
If anyone wants to suggest a cleaner way to do this, I'm all ears.
Thanks!