PDA

View Full Version : Find string between "markers"



agent86
11-11-2013, 09:09 PM
Word 2010

New to VBA...


Sample document

……Inst#: 1……
……Inst#: 2……
……Inst#: 3……

I have created a very short document example with 3 “report sections”. The is the starting marker. is the closing marker. Any number of characters can be between the starting and ending markers. The actual document will have 3-5 sets of markers. That is determined by the user.

The … indicate there couId be any number of characters between and the string “Inst#: 2” and any number of characters following the string “Inst#: 2” but always followed at some point with the closing marker. What I want to do is find the “report section” that contains the desired string and replace all the text between the markers with 3 words, “xman goes here”. I would end up with a set of markers that says “xman goes here”.

The code I have shown below finds Inst#: 1 just fine. The problem is the procedure hangs up in the first set of markers and goes into an infinite loop because it can’t find the string “Inst#: 2” string in the first set of markers. I want to search for the string Inst#: 2 in all sets of markers until it is found and not hang up if the set of markers I am looking for is not the first set. When it examines the sets of markers I want it to keep going to the next set and see if the “Inst#: 2” is there. It always hangs up in the loop with the section of code I have marked with “????”’s. I do not know how to tell it to “bypass” the first set of markers and move on to the next set of markers until it finds the string it is looking for.



Sub RemoveReportByInstance(ByVal strReportInstanceNumber As String)

Dim doc As Document
Dim txtRange As Range
Dim startTag As String
Dim endTag As String
Dim s As Long
Dim e As Long
Dim ReportFound As Boolean

strReportInstanceNumber = "Inst#: " & strReportInstanceNumber

ReportFound = False
System.Cursor = wdCursorWait
Application.ScreenUpdating = False
ActiveWindow.View.ShowHiddenText = True

' start at top of document
Selection.HomeKey Unit:=wdStory

' search report blocks until we find instance number
' instance number will always be unique for mt caseid
' moved to her to test if infinite loop goes away

Set doc = ActiveDocument
Set txtRange = doc.Content


Do
' markers
startTag = ""
endTag = ""
'Find the opening tag
With txtRange.Find
.ClearFormatting
.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)
' check range for Instance we are looking for
' is the instance we are looking for in there? Inst#: 2 there?
' ????????????????????????????????????????????????????????????????
With txtRange.Find
.Text = strReportInstanceNumber
.Forward = True

.Execute
If .Found Then
' you are on correct report so re-orient the start and ending
Set txtRange = doc.Range(s, e)
txtRange.Text = startTag & "xman goes here" & endTag
ReportFound = True
End If

End With
' ????????????????????????????????????????????????????????????????
Loop Until (ReportFound = True)
ActiveWindow.View.ShowHiddenText = False
Application.ScreenUpdating = True

Application.ScreenRefresh

Exit Sub
EarlyExit:
MsgBox "Embedded Report Header not found in this document!", vbInformation
End Sub

macropod
11-11-2013, 09:56 PM
You can do this with/without VBA using a wildcard Find/Replace, where:
Find = \[EmbeddedReport\]…Inst#: 1*\[/\EmbeddedReport\]
Replace = nothing
Do note, though that, if you have, say, an 'Inst#: 2' range embedded in an 'Inst#: 1' range, what'll get deleted is all of the 'Inst#: 2' range and only part of the 'Inst#: 1' range.

agent86
11-12-2013, 12:15 AM
It has to be done with VBA. There are other parts of the program not mentioned where they pick reports from a userform. Inst# 2: will never be in a Inst#: 1 range. The Inst#: strings are all unique. The Inst#: part of the string is part of the title of a report which will replace the text between the 2 markers. Each report is given an instance number and the abbreviation is Inst#: then the number. If they edit the report in the other program it must find it's old version with the same instance #, delete it by replacing the text with "xman goes here". The "xman goes here" text will eventually be replaced with the edited text of the new version of the report.

So what I need is a way to stop the program of getting out of the infinite loop when it encounters the first set of markers which does not contain the target string.

macropod
11-12-2013, 01:12 AM
It has to be done with VBA.
So incorporate the Find expression I posted into a vba wildcard Find/Replace!

So what I need is a way to stop the program of getting out of the infinite loop when it encounters the first set of markers which does not contain the target string.
Why do you need a loop? Consider:

Sub Demo()
Application.ScreenUpdating = False
With ActiveDocument.Range.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "\[EmbeddedReport\]…Inst#: 1*\[/\EmbeddedReport\]"
.Replacement.Text = vbNullString
.Forward = True
.Wrap = wdFindContinue
.Format = False
.MatchWildcards = True
.Execute Replace:=wdReplaceAll
End With
Application.ScreenUpdating = True
End Sub
Even with no looping in the code, this will delete all matched occurrences.

agent86
11-12-2013, 07:54 AM
Wow. Perfect. My inexperience sure gets in the way. As they say "When you only own a hammer the world looks like a nail." Thank you so much.

fumei
11-12-2013, 12:49 PM
It is your inexperience that keeps me asking what and why you are doing this, because I am not sure that your concepts of purpose is the best. But then...oh right...I do not know what your conceptual purpose is. I bwant to be clear, I am NOT saying that this is wrong, I simply do not know if it is, or not. I suspect it can be done better. Oh well. I think I have had enough.

macropod
11-12-2013, 05:29 PM
So, agent86, given that your first post in this thread indicates you're working with Word 2010, why the obsession with Section breaks in your other thread (http://www.vbaexpress.com/forum/showthread.php?48143-Delete-section-containing-given-string)? All of what you're trying to achieve can be done without them if you use the 'No Changes(Read Only)' restriction, combined with selecting an nominating as Exceptions the portions of the document you want to be editable. If you use bookmarks as previously discussed, this also overcomes problems you might otherwise run into when an inserted document has Section breaks of its own...

I suspect the underlying issue is that in your inexperience you're focussing on a particular method of solving a problem rather than allowing for the fact there may be better ways.

agent86
11-12-2013, 05:50 PM
I am adding to another companies approach, the section breaks. I saw how they protected their text and I copied it. I have to be careful not to break their stuff while adding my own. I guess I used their approach because I did not know how to mark the text between the markers read-only. That would still be useful if you will tell me, then I can get rid of the section breaks which would be helpful and a much cleaner approach.

Also I am having to modify your "find and replace method as if I pick Inst#: 3, it deletes everything between the first occurrence of the string before the Inst#: 1 and the following "Inst#: 3". Since I am the one who inserts the [EmbeddedReport] markers I am looking to add the Instance number to the marker. For example ...Inst#: 3.... That way it won't just find the first [EmdeddedReport] marker and the correct trailing marker.

the document would then say...

...Inst#: 1...
...Inst#: 2...
...Inst#: 3...

Then I can use your solution to fix the second and third instances. If you can show me how to mark the text between the markers read-only that would solve the section break problem and I think I can get rid of them. Thanks again for your help.

macropod
11-12-2013, 06:35 PM
So why not use:
....
....
....
With that you could then use whichever of:
.Text = "\[(EmbeddedReport1\])*\[/\1"
.Text = "\[(EmbeddedReport2\])*\[/\1"
.Text = "\[(EmbeddedReport3\])*\[/\1"
you need.

agent86
11-12-2013, 07:20 PM
Certainly could! How do I mark the text between the markers read-only?

macropod
11-12-2013, 09:19 PM
How do I mark the text between the markers read-only?
You don't. If you use the 'No Changes(Read Only)' restriction, you mark the ranges that aren't 'read-only' as Exceptions.

fumei
11-12-2013, 09:30 PM
So that I understand (as I do not use 2010), does that mean the range the inserted content is going into are will be read only and thus not editable by the user, and the existing areas (ranges) are marked as Exceptions. But how does the inserted content get placed INTO a read-only area? If it is read-only, how does it accept new content? I am not familiar with Exceptions.

And of course I could still be out-to-lunch regarding what the real requirements are. I think that the point is to put in content the users can NOT edit.

macropod
11-12-2013, 09:36 PM
As with any kind of content insertion into a protected document, you temporarily disable the protection.

I think that the point is to put in content the users can NOT edit
As I asked in the other thread, how do you propose to prevent the users inserting such content into an editable area? Unless you require all content to be input via a UserForm, so the whole document can remain protected at all times it isn't being edited programmatically, you'll have a hard time doing that.

fumei
11-12-2013, 10:15 PM
This whole thing seems screwy.

fumei
11-12-2013, 10:23 PM
Frankly, it seems to me if you want the kind of control apparently desired that a userform is the way to go. At least things happen only BECAUSE you want them to happen. I mean with the assumption that you - ummm - actually know what you are doing.