this is my first post here!
I am new to Microsoft VBA, but I need to write some macros to quickly make the same changes to 200+ documents. I took the Udemy course on Word VBA but I am stuck trying to figure out how to address the following issues:
I need to select all content from beginning of doc up until the first occurrence of the string "following:" including the string itself - the issue is that for each doc, this string will always be in a different position and the portion I want to delete will vary in length. The only guarantee is that it will always be on the first page.
I need to do the same thing for the end of the document - I need to delete all content following the string "Affirmative Defenses" including the string itself - again, this will always be in a different position and will not always be on the last page (unlike issue number 1)
I have written many variations by reading other questions/solutions with no luck. Below is my current version but it does not work.
Sub DeleteBegin()
Dim findRng As Range
Set findRng = ActiveDocument.Range
Dim endPara As Long
With findRng.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "following."
.Replacement.Text = ""
.Wrap = wdFindStop
.MatchWholeWord = True
.Forward = True
.Execute
If .Found = True Then
endPara = GetParaNum(findRng)
findRng.Start = 0
findRng.End = endPara
End If
Dim capRng As Range
Set capRng = ActiveDocument.Range
capRng.SetRange Start:=0, End:=endPara
capRng.Select
Selection.Delete
End With
End Sub
Function GetParaNum(ByRef r As Object) As Integer
Dim rPara As Object
Dim CurPos As Long
r.Select
CurPos = ActiveDocument.Bookmarks("\startOfSel").Start
Set rPara = ActiveDocument.Range(Start:=0, End:=CurPos)
GetParaNum = rPara.Paragraphs.Count
End Function
Try:
Sub Demo()
Application.ScreenUpdating = False
Dim Rng As Range
With ActiveDocument
Set Rng = .Range(0, 0)
With .Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "following."
.Replacement.Text = ""
.Format = False
.Forward = True
.Wrap = wdFindStop
.Execute
End With
If .Find.Found = True Then
Rng.End = .Duplicate.End
Rng.Delete
End If
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "Affirmative Defenses"
.Replacement.Text = ""
.Format = False
.Forward = True
.Wrap = wdFindStop
.MatchCase = True
.Execute
End With
If .Find.Found = True Then
Set Rng = .Duplicate
Rng.End = ActiveDocument.Range.End
Rng.Delete
End If
End With
End With
Application.ScreenUpdating = True
End Sub
Related
I have the following code to find headings (ranging from heading 1-4) with the word "DELETE" in the heading to delete the heading and the text underneath, as well as nested headings. However, it stops after deleting the first set of headings it finds. How can I get it to work through all the headings in the document? Thank you!
Sub deleteheading()
Dim rngHeading1 As Range
Set rngHeading1 = GetHeadingBlock("DELETE", wdStyleHeading1)
If Not rngHeading1 Is Nothing Then rngHeading1.Delete
Dim rngHeading2 As Range
Set rngHeading2 = GetHeadingBlock("DELETE", wdStyleHeading2)
If Not rngHeading2 Is Nothing Then rngHeading2.Delete
Dim rngHeading3 As Range
Set rngHeading3 = GetHeadingBlock("DELETE", wdStyleHeading3)
If Not rngHeading3 Is Nothing Then rngHeading3.Delete
Dim rngHeading4 As Range
Set rngHeading4 = GetHeadingBlock("DELETE", wdStyleHeading4)
If Not rngHeading4 Is Nothing Then rngHeading4.Delete
End Sub
Function GetHeadingBlock(headingText As String, headingStyle As WdBuiltinStyle) As Range
Dim rngFind As Range
Set rngFind = ActiveDocument.Content
With rngFind.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "DELETE"
.style = headingStyle
.Replacement.Text = ""
.Forward = True
.Format = True
.Wrap = wdFindStop
If .Execute Then Set GetHeadingBlock = _
rngFind.GoTo(What:=wdGoToBookmark, Name:="\HeadingLevel")
End With
End Function
For example:
Sub DeleteHeadingSpanText()
Application.ScreenUpdating = False
Dim h As Long
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "DELETE"
.Replacement.Text = ""
.Format = True
.Forward = True
.MatchCase = True
.MatchWholeWord = True
.Wrap = wdFindContinue
End With
For h = 1 To 9
.Style = "Heading " & h
Do While .Find.Execute
.Duplicate.GoTo(What:=wdGoToBookmark, Name:="\HeadingLevel").Text = vbNullString
Loop
Next
End With
Set RngHd = Nothing
Application.ScreenUpdating = True
End Sub
Change the 1 & 9 in 'For h = 1 To 9' to define whatever heading levels you want to limit the code's scope to.
This code calls your own function GetHeadingBlock.
Sub DeleteHeading()
Dim rngHeading As Range
Dim i As WdBuiltinStyle
For i = wdStyleHeading1 To wdStyleHeading4 Step -1
Do
Set rngHeading = GetHeadingBlock("DELETE", i)
If rngHeading Is Nothing Then
Exit Do
Else
rngHeading.Delete
End If
Loop
Next i
End Sub
Is there a way for vba to find a string of text with a particular style and select until the style is used again? Or is there a way to find a string of text in a numbered list and select until the next number? The heading I want to find and select content under is "SUBMITTALS," but it is not always in the same numerical order in the document, but the style is always the same. Any advice would be appreciated, here is what I have tried:
Selection.Find.ClearFormatting
Selection.Find.Style = ActiveDocument.Styles("Heading 2")
With Selection.Find
.Text = "SUBMITTALS"
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = True
End With
Selection.Find.Execute
Selection.Collapse wdCollapseStart
Dim r1 As range
Set r1 = Selection.range
'v = Selection.range.ListFormat.ListValue
'lstring = Selection.range.ListFormat.ListString
Selection.Find.Style = ActiveDocument.Styles("Heading 2")
With Selection.Find
.Text = "SUBMITTALS"
End With
Dim r5 As range
Set r5 = Selection.Next(wdSection).Next
'If Selection.Find.Execute Then
'Selection.Collapse wdCollapseStart
'Else
'Selection.WholeStory
'Selection.Extend
'Selection.Collapse wdCollapseEnd
'End If
Dim r2 As range
Set r2 = ActiveDocument.range(r1.Start, r5.Start)
r2.Select
For example:
Sub Demo()
Application.ScreenUpdating = False
Dim Rng As Range
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "SUBMITTALS"
.Style = wdStyleHeading2
.Replacement.Text = ""
.Format = True
.Forward = True
.Wrap = wdFindStop
.Execute
End With
If .Find.Found = True Then
Set Rng = .Paragraphs(1).Range
Set Rng = RngHd.GoTo(What:=wdGoToBookmark, Name:="\HeadingLevel")
MsgBox Rng.Text
End If
End With
Set Rng = Nothing
Application.ScreenUpdating = True
End Sub
We are trying to revise rtf docs that are created by Molecular Device software.
Here is an example of part of one of these documents:
Protocol 'C:\ALL USERS\Params\Current\2017 Opto Params\0 VoltageClampContinuous.pro' opened.
C:\ALL USERS\Alan\2018_07_11\2018_07_11_0000.abf started at 00:19:48 stopwatch time.
So for right now - all I am trying to do is automatically find the experiment date (in this case = "2018_07_11_")
My sub so far can find the correct cursor positions but how do I select the text between 2 cursor positions?
Below is what I have the CursorPosition statement is of course wrong - this is what I am looking to correct.
Sub FindfilenameDate()
txt_prior_to_expDate = "\"
txt_after_expDate = "0000"
With ActiveDocument.Content.Find
.Text = txt_after_expDate
.Forward = True
.Execute
If .Found = True Then
.Parent.Select
Set after_rng = Selection.Range
expDateEnd_cursorPos = after_rng.Start - 1
Selection.HomeKey Unit:=wdStory, Extend:=wdExtend
With Selection.Find
.Text = txt_prior_to_expDate
.Forward = False
.Execute
If .Found = True Then
.Parent.Select
Set charBefore_expDate = Selection.Range
expDateStart_cursorPos = charBefore_expDate.Start + 1
End If
End With
End If
End With
'expDate = CursorPosition(expDateStart_cursorPos, expDateEnd_cursorPos)
'MsgBox ("expDate = " & expDate) 'DELETEMSGBOX
End Sub
The trick to something like this is to work with multiple Range objects. My personal preference is to declare a Range for each separate thing to be worked with, rather than trying to figure out the minimum and re-use a Range - at least for the initial code and testing purposes.
For this task, then, I use four Ranges: 1) For the original search, 2) for the end of the "cursor position" that's wanted, 3) For the second search, 4) for the final "cursor position".
The other important concepts are how to "collapse" a Range and how to "copy" one.
Collapsing a Range is like pressing the right- or left-arrow key with a selection, so that it is a "point" and doesn't contain anything. A Range can be collapsed to its start or end position.
Copying a Range (setting one Range to another) needs to be done using the Duplicate property so that the copy is independent of the original. Otherwise, when one is changed the other changes, as well.
Sub FindfilenameDate()
Dim rngFind As Word.Range, rngBefore As Word.Range
Dim rngAfter As Word.Range, rngFound As Word.Range
txt_prior_to_expDate = "\"
txt_after_expDate = "0000"
Set rngFind = ActiveDocument.content
With rngFind.Find
.Text = txt_after_expDate
.Forward = True
.Execute
If .found = True Then
Set rngAfter = rngFind.Duplicate
rngAfter.Collapse wdCollapseStart
Set rngBefore = rngFind.Duplicate
rngBefore.Collapse wdCollapseStart
With rngBefore.Find
.Text = txt_prior_to_expDate
.Forward = False
.Execute
If .found = True Then
Set rngFound = rngBefore.Duplicate
rngFound.Collapse wdCollapseEnd
rngFound.End = rngAfter.Start
'rngFound.Select
End If
End With
End If
End With
'expDate = CursorPosition(expDateStart_cursorPos, expDateEnd_cursorPos)
MsgBox ("expDate = " & rngFound.Text) 'DELETEMSGBOX
End Sub
Though it's not apparent why you're after the date string ending in _0000 rather than the date that is the parent folder name, a much simpler approach for a single date would be:
Sub Demo()
Application.ScreenUpdating = False
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "[0-9]{4}_[0-9]{2}_[0-9]{2}_0000"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
.Execute
End With
If .Find.Found = True Then MsgBox "expDate = " & Split(.Text, "_0000")(0)
End With
Application.ScreenUpdating = True
End Sub
And, for all such dates in a document:
Sub Demo()
Application.ScreenUpdating = False
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "[0-9]{4}_[0-9]{2}_[0-9]{2}_0000"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
.Execute
End With
Do While .Find.Found = True
MsgBox "expDate = " & Split(.Text, "_0000")(0)
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
End Sub
I have a text with five 'XX' scattered at different places. I'd like to
replace the first instance of XX by 1., the second instance by 2. and
so, until replacing the fifth instance by 5. I tried to use the
following code without success:
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "XX"
.Replacement.Text = "1."
.Execute Replace:=wdReplaceOne
.Forward = True
.Text = "XX"
.Replacement.Text = "2."
.Execute Replace:=wdReplaceOne
.Forward = True
[...]
.Text = "XX"
.Replacement.Text = "5."
.Execute Replace:=wdReplaceOne
.Wrap = wdFindStop
End With
End Sub
How can I modify the above code, or what else should I do to get these substitutions? If I use "sed" on a text file, I'd simply do: s/XX/1./1, s/XX/2./2, s/XX/3./3, etc.
What you can do is use a For...Next loop to increment a counter, using the counter as the replacement text. This will save having to write lots of repeated code, as the first code sample shows.
The code can be made more flexible, for any number of occurrences of the search term, by using a Do...Until loop that repeats until no more instances of the search term are found. This variation is in the second code example.
Both variations contain code to search either the entire document or the current selection. Just comment out the one you don't need and remove the comment from the one you do...
Sub FindAndReplaceWithNumbers()
Dim rngDoc As word.Range, rngFind As word.Range
Dim findString As String
Dim counter As Long
Dim found As Boolean
findString = "XX"
'To search the entire document
'Set rngDoc = ActiveDocument.content
'To search a selection, only
Set rngDoc = Selection.Range
Set rngFind = rngDoc.Duplicate
With rngFind.Find
.ClearFormatting
.Replacement.ClearFormatting
.wrap = wdFindStop
.Forward = True
.Text = findString
End With
For counter = 1 To 5
With rngFind.Find
.Replacement.Text = CStr(counter) & "."
found = .Execute(Replace:=wdReplaceOne)
End With
rngFind.End = rngDoc.End
Next
End Sub
Sub FindAndReplaceWithNumbers()
Dim rngDoc As word.Range, rngFind As word.Range
Dim findString As String
Dim counter As Long
Dim found As Boolean
findString = "XX"
counter = 1
'To search the entire document
'Set rngDoc = ActiveDocument.content
'To search a selection, only
Set rngDoc = Selection.Range
Set rngFind = rngDoc.Duplicate
With rngFind.Find
.ClearFormatting
.Replacement.ClearFormatting
.wrap = wdFindStop
.Forward = True
.Text = findString
End With
Do
With rngFind.Find
.Replacement.Text = CStr(counter) & "."
found = .Execute(Replace:=wdReplaceOne)
End With
rngFind.End = rngDoc.End
counter = counter + 1
Loop Until Not found
End Sub
Simpler and faster:
Sub Demo()
Application.ScreenUpdating = False
Dim i As Long
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "XX"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.MatchWildcards = True
.Execute
End With
Do While .Find.Found
i = i + 1
.Text = i
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
MsgBox i & " records updated."
End Sub
To limit the code's scope to a Selection, you could use:
Sub Demo()
Application.ScreenUpdating = False
Dim Rng As Range, i As Long
With Selection
Set Rng = .Range
With .Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "XX"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.MatchWildcards = True
.Execute
End With
Do While .Find.Found
If .InRange(Rng) Then
i = i + 1
.Text = i
Else
Exit Do
End If
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
End With
Application.ScreenUpdating = True
MsgBox i & " records updated."
End Sub
I'm trying to find a regex string, <XE "i#>, in a Word document.
Background: I'm building an index, and automatically picking up paragraphs to add via another macro. There are some entries that start "i. Automobile - means a car", or "ii. Super - means really good". I want to remove the numbering part from the Index entry, so thought a way to do so would be to look for the {XE "i. Automobile ...} part and just remove the i. using RegEx.
When I search manually for my string, it works fine and picks up the matches. However, my macro doesn't work. When stepping through, then I get to While .Execute, the next step just goes to Wend then End With. It does ask if I want to search from the beginning, so the .Find is working somewhat, but why isn't it finding any matches?
Thanks so much for any advice!
Sub Hide_Roman_Numerals_from_Index()
Dim defText As String
Dim regExSearch As String
Dim oRng As Word.Range, rng As Word.Range
If ActiveWindow.ActivePane.View.ShowAll = False Then
ActiveWindow.ActivePane.View.ShowAll = True
End If
Set oRng = ActiveDocument.Range
'Call ClearFindAndReplaceParameters(oRng)
regExSearch = "<XE ""i#>"
oRng.Find.ClearFormatting
With oRng.Find
.Text = regExSearch
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindAsk
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
While .Execute
Set rng = oRng.Paragraphs(1).Range
rng.Select
Wend
End With
If ActiveWindow.ActivePane.View.ShowAll = True Then
ActiveWindow.ActivePane.View.ShowAll = False
End If
'Call ClearFindAndReplaceParameters(oRng)
End Sub
I think this approach will suit you if I got your problem right.
'BruceWayne
Sub Colorgreenfromw()
Application.ScreenUpdating = False
Dim oPar As Paragraph
Dim oRng As Word.Range
For Each oPar In ActiveDocument.Paragraphs
Set oRng = oPar.Range
With oRng
With .Find
.ClearFormatting
.Font.Color = wdColorGreen
.Replacement.ClearFormatting
.Text = "<XE ""i#>"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
.Execute
End With
If .Find.Found Then
Set oRng = oPar.Range
oRng.Font.Color = wdColorGreen
Set oRng = Nothing
End If
End With
Next
End Sub