find words containing uppercase

R

Rodney Atkins

I have SGML-like documents in which the headings are coded <<hd#>>,
where # is a number:

<<hd3>>This is my heading

I need to

--find each head (<<hd#>>) in the doc
--if any "word" in that head has a capital letter, surround it with
brackets

Before:
<<hd1>>This is my Heading with a Cap in it

After:
<<hd1>>This is my
with a [Cap] in it

It's not necessary for the first word unless there's a cap in the
middle.

<<hd1>>[DelMonte] packages peaches

I'm not quite sure where to begin. I can search for "<<hd" and use
Selection.Extend, or perhaps Selection.MoveEnd, but once I have the
heading selected, I'm at a bit of a loss as to how to test it.

Thanks.​
 
J

Jay Freedman

Hi, Rodney,

I'm not sure whether this macro meets all your needs, but you should
be able to tweak it as necessary.

The idea is to use one range object (oRg1) to search for the tags.
Each time a tag is found, a second range object (oRg2) is set to the
remaining part of that line.

Because of a quirk of VBA in which the built-in bookmark "\line" is
available only for the Selection and not for range objects, it's
necessary to select oRg1 in order to find the end of the line. (Note:
if what you really mean is the *paragraph* rather than the *line*,
this becomes much simpler -- just use
Set oRg2 = oRg1.Paragraphs(1).Range
and forget about the Selection altogether.)

Once you have oRg2, you can check its first word for a capital letter
other than the first; then you can run a wildcard find/replace to find
and bracket any other capitalized words.

Sub BracketCapWords()
Dim oRg1 As Range, oRg2 As Range
Dim tempstr As String

Set oRg1 = ActiveDocument.Range
With oRg1.Find
.ClearFormatting
.Format = False
.Forward = True
.Wrap = wdFindStop
.MatchWildcards = False
.Text = "<<hd^#>>"

Do While .Execute
oRg1.Select
Set oRg2 = Selection.Bookmarks("\line").Range
' place oRg1 at end of line for next pass
oRg1.Start = oRg2.End
oRg1.End = oRg2.End

' move start of oRg2 after tag
oRg2.MoveStartUntil cset:=">"
oRg2.MoveStart unit:=wdCharacter, Count:=2

' handle 1st word
tempstr = Trim(oRg2.Words(1).Text)
tempstr = Right(tempstr, Len(tempstr) - 1)
If AnyCaps(tempstr) Then
oRg2.Words(1).Text = _
"[" & Trim(oRg2.Words(1).Text) & "] "
End If
oRg2.MoveStart unit:=wdWord, Count:=1

' handle remaining words with wildcard replace
With oRg2.Find
.ClearFormatting
.Replacement.ClearFormatting
.Format = False
.Forward = True
.Wrap = wdFindStop
.MatchWildcards = True
.Text = "(<[A-Z][a-z]{1,}>)"
.Replacement.Text = "[\1]"
.Execute Replace:=wdReplaceAll
End With
Loop
End With

Selection.HomeKey unit:=wdStory
End Sub

Private Function AnyCaps(test As String) As Boolean
Dim bRes As Boolean
Dim n As Integer
bRes = False
For n = 1 To Len(test)
If Mid(test, n, 1) = UCase(Mid(test, n, 1)) Then
bRes = True
Exit For
End If
Next n
AnyCaps = bRes
End Function


Rodney Atkins said:
I have SGML-like documents in which the headings are coded <<hd#>>,
where # is a number:

<<hd3>>This is my heading

I need to

--find each head (<<hd#>>) in the doc
--if any "word" in that head has a capital letter, surround it with
brackets

Before:
<<hd1>>This is my Heading with a Cap in it

After:
<<hd1>>This is my
with a [Cap] in it

It's not necessary for the first word unless there's a cap in the
middle.

<<hd1>>[DelMonte] packages peaches

I'm not quite sure where to begin. I can search for "<<hd" and use
Selection.Extend, or perhaps Selection.MoveEnd, but once I have the
heading selected, I'm at a bit of a loss as to how to test it.

Thanks.​
 
P

Peter Hewett

Hi Rodney

This was reasonably complex, but give this a try. It still needs a little
work on checking if the first word contains an uppercase character. But
this is 95% of what's required (all the hard stuff) and it would be useful
to you to work out the rest:

Public Sub NestedSearchWithInnerAction()
Dim rngOuterFind As Word.Range
Dim rngInnerFind As Word.Range
Dim rngWord As Word.Range
Dim rngInnerPart As Word.Range

' Setup outer find to search the entire document
Set rngOuterFind = ActiveDocument.Content
With rngOuterFind.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "\<\<hd[0-9]\>\>"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
End With

' Do outer find...
Do While rngOuterFind.Find.Execute

' Range object starts at the end of the outer range
' and extends to the end of the paragraph
Set rngInnerFind = rngOuterFind.Duplicate
rngInnerFind.Collapse wdCollapseEnd
rngInnerFind.MoveEnd wdParagraph, 1

' MsgBox "Outer find: " & rngInnerFind.Text

' Setup inner find to search specified part of the paragraph
With rngInnerFind.Find
.Text = "<[A-Z]*>"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = True
.MatchSoundsLike = False
.MatchAllWordForms = False
End With

' So inner search
Set rngInnerPart = rngInnerFind.Duplicate
Do While rngInnerFind.Find.Execute

' Select the word starting with the character found
With rngInnerFind
Set rngWord = .Duplicate

' Exclude this word if its the first in the range
If rngWord.Start <> rngInnerPart.Start Then
rngWord.InsertBefore "["
rngWord.InsertAfter "]"
End If

' MsgBox "Inner find: " & rngWord.Text
' Setup the range where the search should resume
.Start = rngWord.End
.End = rngInnerPart.End

' Exit loop if we've hit the end of the search
If .Start = .End Then
Exit Do
End If
End With
Loop
Loop
End Sub

What it does:

1. It searches for "<<hdN>>", where N is a number between 0 and 9.
2. It then searches the rest of the paragraph for words starting with an
upper case character, if it finds a matching word it inserts "[]" around
it.
3. If the word following the "<<hdN>>" character sequence starts with an
upper case character the word is ignored. You need to do your thing here
4. It processes the entire document

NOTE: I've left the MsgBox statements in, but commented out to aid any
debugging you need to do. Do note it processes paragraphs rather than
lines.

Sample output:

<<hd1>>This is my
with a [Cap] in it
<<hd3>>This is [My] heading
The quick brown fox jumps over the lazy dog.
<<hd2>>It may [Be] something else [Hey]
<<hd9>>Her [Name] is [Destiny]
The quick brown fox jumps over the lazy dog.
<<hdX>>His Name is Fate
<<hd0>>Mine [Is] [Not] [To] [Reason] [Why], mine is [But] to [Do] or [DIE]

HTH + Cheers - Peter


I have SGML-like documents in which the headings are coded <<hd#>>,
where # is a number:

<<hd3>>This is my heading

I need to

--find each head (<<hd#>>) in the doc
--if any "word" in that head has a capital letter, surround it with
brackets

Before:
<<hd1>>This is my Heading with a Cap in it

After:
<<hd1>>This is my
with a [Cap] in it

It's not necessary for the first word unless there's a cap in the
middle.

<<hd1>>[DelMonte] packages peaches

I'm not quite sure where to begin. I can search for "<<hd" and use
Selection.Extend, or perhaps Selection.MoveEnd, but once I have the
heading selected, I'm at a bit of a loss as to how to test it.

Thanks.​
 
R

Rodney Atkins

Jay,

You've gone above and beyond here. I was just looking for a push in
the right direction, and you've gotten me to my destination.

Thanks very much. I'm not sure I know what's going on here, but I'll
study it. Is there any significance to the variable name oRg?

Thanks.

Rodney

Hi, Rodney,

I'm not sure whether this macro meets all your needs, but you should
be able to tweak it as necessary.

The idea is to use one range object (oRg1) to search for the tags.
Each time a tag is found, a second range object (oRg2) is set to the
remaining part of that line.

Because of a quirk of VBA in which the built-in bookmark "\line" is
available only for the Selection and not for range objects, it's
necessary to select oRg1 in order to find the end of the line. (Note:
if what you really mean is the *paragraph* rather than the *line*,
this becomes much simpler -- just use
Set oRg2 = oRg1.Paragraphs(1).Range
and forget about the Selection altogether.)

Once you have oRg2, you can check its first word for a capital letter
other than the first; then you can run a wildcard find/replace to find
and bracket any other capitalized words.

Sub BracketCapWords()
Dim oRg1 As Range, oRg2 As Range
Dim tempstr As String

Set oRg1 = ActiveDocument.Range
With oRg1.Find
.ClearFormatting
.Format = False
.Forward = True
.Wrap = wdFindStop
.MatchWildcards = False
.Text = "<<hd^#>>"

Do While .Execute
oRg1.Select
Set oRg2 = Selection.Bookmarks("\line").Range
' place oRg1 at end of line for next pass
oRg1.Start = oRg2.End
oRg1.End = oRg2.End

' move start of oRg2 after tag
oRg2.MoveStartUntil cset:=">"
oRg2.MoveStart unit:=wdCharacter, Count:=2

' handle 1st word
tempstr = Trim(oRg2.Words(1).Text)
tempstr = Right(tempstr, Len(tempstr) - 1)
If AnyCaps(tempstr) Then
oRg2.Words(1).Text = _
"[" & Trim(oRg2.Words(1).Text) & "] "
End If
oRg2.MoveStart unit:=wdWord, Count:=1

' handle remaining words with wildcard replace
With oRg2.Find
.ClearFormatting
.Replacement.ClearFormatting
.Format = False
.Forward = True
.Wrap = wdFindStop
.MatchWildcards = True
.Text = "(<[A-Z][a-z]{1,}>)"
.Replacement.Text = "[\1]"
.Execute Replace:=wdReplaceAll
End With
Loop
End With

Selection.HomeKey unit:=wdStory
End Sub

Private Function AnyCaps(test As String) As Boolean
Dim bRes As Boolean
Dim n As Integer
bRes = False
For n = 1 To Len(test)
If Mid(test, n, 1) = UCase(Mid(test, n, 1)) Then
bRes = True
Exit For
End If
Next n
AnyCaps = bRes
End Function


Rodney Atkins said:
I have SGML-like documents in which the headings are coded <<hd#>>,
where # is a number:

<<hd3>>This is my heading

I need to

--find each head (<<hd#>>) in the doc
--if any "word" in that head has a capital letter, surround it with
brackets

Before:
<<hd1>>This is my Heading with a Cap in it

After:
<<hd1>>This is my
with a [Cap] in it

It's not necessary for the first word unless there's a cap in the
middle.

<<hd1>>[DelMonte] packages peaches

I'm not quite sure where to begin. I can search for "<<hd" and use
Selection.Extend, or perhaps Selection.MoveEnd, but once I have the
heading selected, I'm at a bit of a loss as to how to test it.

Thanks.​
 
P

Peter Hewett

Hi Rodney

Sorry please delete the following lines, as they're redundant:

' Exit loop if we've hit the end of the search
If .Start = .End Then
Exit Do
End If

I'd mintended to delete them before I posted, ooops!

Cheers - Peter

Hi Rodney

This was reasonably complex, but give this a try. It still needs a little
work on checking if the first word contains an uppercase character. But
this is 95% of what's required (all the hard stuff) and it would be useful
to you to work out the rest:

Public Sub NestedSearchWithInnerAction()
Dim rngOuterFind As Word.Range
Dim rngInnerFind As Word.Range
Dim rngWord As Word.Range
Dim rngInnerPart As Word.Range

' Setup outer find to search the entire document
Set rngOuterFind = ActiveDocument.Content
With rngOuterFind.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "\<\<hd[0-9]\>\>"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
End With

' Do outer find...
Do While rngOuterFind.Find.Execute

' Range object starts at the end of the outer range
' and extends to the end of the paragraph
Set rngInnerFind = rngOuterFind.Duplicate
rngInnerFind.Collapse wdCollapseEnd
rngInnerFind.MoveEnd wdParagraph, 1

' MsgBox "Outer find: " & rngInnerFind.Text

' Setup inner find to search specified part of the paragraph
With rngInnerFind.Find
.Text = "<[A-Z]*>"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = True
.MatchSoundsLike = False
.MatchAllWordForms = False
End With

' So inner search
Set rngInnerPart = rngInnerFind.Duplicate
Do While rngInnerFind.Find.Execute

' Select the word starting with the character found
With rngInnerFind
Set rngWord = .Duplicate

' Exclude this word if its the first in the range
If rngWord.Start <> rngInnerPart.Start Then
rngWord.InsertBefore "["
rngWord.InsertAfter "]"
End If

' MsgBox "Inner find: " & rngWord.Text
' Setup the range where the search should resume
.Start = rngWord.End
.End = rngInnerPart.End

' Exit loop if we've hit the end of the search
If .Start = .End Then
Exit Do
End If
End With
Loop
Loop
End Sub

What it does:

1. It searches for "<<hdN>>", where N is a number between 0 and 9.
2. It then searches the rest of the paragraph for words starting with an
upper case character, if it finds a matching word it inserts "[]" around
it.
3. If the word following the "<<hdN>>" character sequence starts with an
upper case character the word is ignored. You need to do your thing here
4. It processes the entire document

NOTE: I've left the MsgBox statements in, but commented out to aid any
debugging you need to do. Do note it processes paragraphs rather than
lines.

Sample output:

<<hd1>>This is my
with a [Cap] in it
<<hd3>>This is [My] heading
The quick brown fox jumps over the lazy dog.
<<hd2>>It may [Be] something else [Hey]
<<hd9>>Her [Name] is [Destiny]
The quick brown fox jumps over the lazy dog.
<<hdX>>His Name is Fate
<<hd0>>Mine [Is] [Not] [To] [Reason] [Why], mine is [But] to [Do] or [DIE]

HTH + Cheers - Peter


I have SGML-like documents in which the headings are coded <<hd#>>,
where # is a number:

<<hd3>>This is my heading

I need to

--find each head (<<hd#>>) in the doc
--if any "word" in that head has a capital letter, surround it with
brackets

Before:
<<hd1>>This is my Heading with a Cap in it

After:
<<hd1>>This is my
with a [Cap] in it

It's not necessary for the first word unless there's a cap in the
middle.

<<hd1>>[DelMonte] packages peaches

I'm not quite sure where to begin. I can search for "<<hd" and use
Selection.Extend, or perhaps Selection.MoveEnd, but once I have the
heading selected, I'm at a bit of a loss as to how to test it.

Thanks.​

 
R

Rodney Atkins

One other thing that I didn't mention, if this is of use:

Each header lines ends with either a hard return
OR the tag "<med>"

Does make anything easier/simpler?

Thanks again.

Hi, Rodney,

I'm not sure whether this macro meets all your needs, but you should
be able to tweak it as necessary.

The idea is to use one range object (oRg1) to search for the tags.
Each time a tag is found, a second range object (oRg2) is set to the
remaining part of that line.

Because of a quirk of VBA in which the built-in bookmark "\line" is
available only for the Selection and not for range objects, it's
necessary to select oRg1 in order to find the end of the line. (Note:
if what you really mean is the *paragraph* rather than the *line*,
this becomes much simpler -- just use
Set oRg2 = oRg1.Paragraphs(1).Range
and forget about the Selection altogether.)

Once you have oRg2, you can check its first word for a capital letter
other than the first; then you can run a wildcard find/replace to find
and bracket any other capitalized words.

Sub BracketCapWords()
Dim oRg1 As Range, oRg2 As Range
Dim tempstr As String

Set oRg1 = ActiveDocument.Range
With oRg1.Find
.ClearFormatting
.Format = False
.Forward = True
.Wrap = wdFindStop
.MatchWildcards = False
.Text = "<<hd^#>>"

Do While .Execute
oRg1.Select
Set oRg2 = Selection.Bookmarks("\line").Range
' place oRg1 at end of line for next pass
oRg1.Start = oRg2.End
oRg1.End = oRg2.End

' move start of oRg2 after tag
oRg2.MoveStartUntil cset:=">"
oRg2.MoveStart unit:=wdCharacter, Count:=2

' handle 1st word
tempstr = Trim(oRg2.Words(1).Text)
tempstr = Right(tempstr, Len(tempstr) - 1)
If AnyCaps(tempstr) Then
oRg2.Words(1).Text = _
"[" & Trim(oRg2.Words(1).Text) & "] "
End If
oRg2.MoveStart unit:=wdWord, Count:=1

' handle remaining words with wildcard replace
With oRg2.Find
.ClearFormatting
.Replacement.ClearFormatting
.Format = False
.Forward = True
.Wrap = wdFindStop
.MatchWildcards = True
.Text = "(<[A-Z][a-z]{1,}>)"
.Replacement.Text = "[\1]"
.Execute Replace:=wdReplaceAll
End With
Loop
End With

Selection.HomeKey unit:=wdStory
End Sub

Private Function AnyCaps(test As String) As Boolean
Dim bRes As Boolean
Dim n As Integer
bRes = False
For n = 1 To Len(test)
If Mid(test, n, 1) = UCase(Mid(test, n, 1)) Then
bRes = True
Exit For
End If
Next n
AnyCaps = bRes
End Function


Rodney Atkins said:
I have SGML-like documents in which the headings are coded <<hd#>>,
where # is a number:

<<hd3>>This is my heading

I need to

--find each head (<<hd#>>) in the doc
--if any "word" in that head has a capital letter, surround it with
brackets

Before:
<<hd1>>This is my Heading with a Cap in it

After:
<<hd1>>This is my
with a [Cap] in it

It's not necessary for the first word unless there's a cap in the
middle.

<<hd1>>[DelMonte] packages peaches

I'm not quite sure where to begin. I can search for "<<hd" and use
Selection.Extend, or perhaps Selection.MoveEnd, but once I have the
heading selected, I'm at a bit of a loss as to how to test it.

Thanks.​
 
R

Rodney Atkins

Thanks to you, too, Peter. An embarrasment of riches here.

Hi Rodney

Sorry please delete the following lines, as they're redundant:

' Exit loop if we've hit the end of the search
If .Start = .End Then
Exit Do
End If

I'd mintended to delete them before I posted, ooops!

Cheers - Peter

Hi Rodney

This was reasonably complex, but give this a try. It still needs a little
work on checking if the first word contains an uppercase character. But
this is 95% of what's required (all the hard stuff) and it would be useful
to you to work out the rest:

Public Sub NestedSearchWithInnerAction()
Dim rngOuterFind As Word.Range
Dim rngInnerFind As Word.Range
Dim rngWord As Word.Range
Dim rngInnerPart As Word.Range

' Setup outer find to search the entire document
Set rngOuterFind = ActiveDocument.Content
With rngOuterFind.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "\<\<hd[0-9]\>\>"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchAllWordForms = False
.MatchSoundsLike = False
.MatchWildcards = True
End With

' Do outer find...
Do While rngOuterFind.Find.Execute

' Range object starts at the end of the outer range
' and extends to the end of the paragraph
Set rngInnerFind = rngOuterFind.Duplicate
rngInnerFind.Collapse wdCollapseEnd
rngInnerFind.MoveEnd wdParagraph, 1

' MsgBox "Outer find: " & rngInnerFind.Text

' Setup inner find to search specified part of the paragraph
With rngInnerFind.Find
.Text = "<[A-Z]*>"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = True
.MatchSoundsLike = False
.MatchAllWordForms = False
End With

' So inner search
Set rngInnerPart = rngInnerFind.Duplicate
Do While rngInnerFind.Find.Execute

' Select the word starting with the character found
With rngInnerFind
Set rngWord = .Duplicate

' Exclude this word if its the first in the range
If rngWord.Start <> rngInnerPart.Start Then
rngWord.InsertBefore "["
rngWord.InsertAfter "]"
End If

' MsgBox "Inner find: " & rngWord.Text
' Setup the range where the search should resume
.Start = rngWord.End
.End = rngInnerPart.End

' Exit loop if we've hit the end of the search
If .Start = .End Then
Exit Do
End If
End With
Loop
Loop
End Sub

What it does:

1. It searches for "<<hdN>>", where N is a number between 0 and 9.
2. It then searches the rest of the paragraph for words starting with an
upper case character, if it finds a matching word it inserts "[]" around
it.
3. If the word following the "<<hdN>>" character sequence starts with an
upper case character the word is ignored. You need to do your thing here
4. It processes the entire document

NOTE: I've left the MsgBox statements in, but commented out to aid any
debugging you need to do. Do note it processes paragraphs rather than
lines.

Sample output:

<<hd1>>This is my
with a [Cap] in it
<<hd3>>This is [My] heading
The quick brown fox jumps over the lazy dog.
<<hd2>>It may [Be] something else [Hey]
<<hd9>>Her [Name] is [Destiny]
The quick brown fox jumps over the lazy dog.
<<hdX>>His Name is Fate
<<hd0>>Mine [Is] [Not] [To] [Reason] [Why], mine is [But] to [Do] or [DIE]

HTH + Cheers - Peter


I have SGML-like documents in which the headings are coded <<hd#>>,
where # is a number:

<<hd3>>This is my heading

I need to

--find each head (<<hd#>>) in the doc
--if any "word" in that head has a capital letter, surround it with
brackets

Before:
<<hd1>>This is my Heading with a Cap in it

After:
<<hd1>>This is my
with a [Cap] in it

It's not necessary for the first word unless there's a cap in the
middle.

<<hd1>>[DelMonte] packages peaches

I'm not quite sure where to begin. I can search for "<<hd" and use
Selection.Extend, or perhaps Selection.MoveEnd, but once I have the
heading selected, I'm at a bit of a loss as to how to test it.

Thanks.​

 
P

Peter Hewett

Hi jay

Here's a shorter "AnyCaps", I know you'd have seen it in another minute or
so, if not already:

Public Function AnyCaps(test As String) As Boolean
AnyCaps = Not (LCase$(test) = test)
End Function

Cheers - Peter


Hi, Rodney,

I'm not sure whether this macro meets all your needs, but you should
be able to tweak it as necessary.

The idea is to use one range object (oRg1) to search for the tags.
Each time a tag is found, a second range object (oRg2) is set to the
remaining part of that line.

Because of a quirk of VBA in which the built-in bookmark "\line" is
available only for the Selection and not for range objects, it's
necessary to select oRg1 in order to find the end of the line. (Note:
if what you really mean is the *paragraph* rather than the *line*,
this becomes much simpler -- just use
Set oRg2 = oRg1.Paragraphs(1).Range
and forget about the Selection altogether.)

Once you have oRg2, you can check its first word for a capital letter
other than the first; then you can run a wildcard find/replace to find
and bracket any other capitalized words.

Sub BracketCapWords()
Dim oRg1 As Range, oRg2 As Range
Dim tempstr As String

Set oRg1 = ActiveDocument.Range
With oRg1.Find
.ClearFormatting
.Format = False
.Forward = True
.Wrap = wdFindStop
.MatchWildcards = False
.Text = "<<hd^#>>"

Do While .Execute
oRg1.Select
Set oRg2 = Selection.Bookmarks("\line").Range
' place oRg1 at end of line for next pass
oRg1.Start = oRg2.End
oRg1.End = oRg2.End

' move start of oRg2 after tag
oRg2.MoveStartUntil cset:=">"
oRg2.MoveStart unit:=wdCharacter, Count:=2

' handle 1st word
tempstr = Trim(oRg2.Words(1).Text)
tempstr = Right(tempstr, Len(tempstr) - 1)
If AnyCaps(tempstr) Then
oRg2.Words(1).Text = _
"[" & Trim(oRg2.Words(1).Text) & "] "
End If
oRg2.MoveStart unit:=wdWord, Count:=1

' handle remaining words with wildcard replace
With oRg2.Find
.ClearFormatting
.Replacement.ClearFormatting
.Format = False
.Forward = True
.Wrap = wdFindStop
.MatchWildcards = True
.Text = "(<[A-Z][a-z]{1,}>)"
.Replacement.Text = "[\1]"
.Execute Replace:=wdReplaceAll
End With
Loop
End With

Selection.HomeKey unit:=wdStory
End Sub

Private Function AnyCaps(test As String) As Boolean
Dim bRes As Boolean
Dim n As Integer
bRes = False
For n = 1 To Len(test)
If Mid(test, n, 1) = UCase(Mid(test, n, 1)) Then
bRes = True
Exit For
End If
Next n
AnyCaps = bRes
End Function


Rodney Atkins said:
I have SGML-like documents in which the headings are coded <<hd#>>,
where # is a number:

<<hd3>>This is my heading

I need to

--find each head (<<hd#>>) in the doc
--if any "word" in that head has a capital letter, surround it with
brackets

Before:
<<hd1>>This is my Heading with a Cap in it

After:
<<hd1>>This is my
with a [Cap] in it

It's not necessary for the first word unless there's a cap in the
middle.

<<hd1>>[DelMonte] packages peaches

I'm not quite sure where to begin. I can search for "<<hd" and use
Selection.Extend, or perhaps Selection.MoveEnd, but once I have the
heading selected, I'm at a bit of a loss as to how to test it.

Thanks.​

 
J

Jay Freedman

Thanks, Peter -- it was getting pretty far past bedtime when I wrote that
function. I knew there was a cleaner way but I was just too tired to think
about it. :)
 
J

Jay Freedman

Hi, Rodney,

There's nothing special about the name oRg as far as VBA is concerned. The
only "significance" is what it tells me when I read the code: "o" for
"object" and "Rg" for "Range". It reminds me that when I make an assignment
to an object, I must use the Set keyword, as in the statement
Set oRg1 = ActiveDocument.Range

The original naming system using data-type prefixes is called Hungarian
notation (http://msdn.microsoft.com/library/techart/hunganotat.htm). What
you see in my code is a simplified version, and probably not always
consistently applied. :)
 
J

Jay Freedman

Hi, Rodney,

If each header line ended with a hard return, then this comment from my
previous post would apply:

This is the approach that Peter took in his macro.

If a header line might end with <med> followed by more text but not a hard
return, then you could stay with my original approach, or use a .Find to
search within the paragraph for a <med> tag and adjust the end of the range
to stop before it (which is more complicated, not less).

--
Regards,
Jay Freedman
Microsoft Word MVP

Rodney said:
One other thing that I didn't mention, if this is of use:

Each header lines ends with either a hard return
OR the tag "<med>"

Does make anything easier/simpler?

Thanks again.

Hi, Rodney,

I'm not sure whether this macro meets all your needs, but you should
be able to tweak it as necessary.

The idea is to use one range object (oRg1) to search for the tags.
Each time a tag is found, a second range object (oRg2) is set to the
remaining part of that line.

Because of a quirk of VBA in which the built-in bookmark "\line" is
available only for the Selection and not for range objects, it's
necessary to select oRg1 in order to find the end of the line. (Note:
if what you really mean is the *paragraph* rather than the *line*,
this becomes much simpler -- just use
Set oRg2 = oRg1.Paragraphs(1).Range
and forget about the Selection altogether.)

Once you have oRg2, you can check its first word for a capital letter
other than the first; then you can run a wildcard find/replace to
find and bracket any other capitalized words.

Sub BracketCapWords()
Dim oRg1 As Range, oRg2 As Range
Dim tempstr As String

Set oRg1 = ActiveDocument.Range
With oRg1.Find
.ClearFormatting
.Format = False
.Forward = True
.Wrap = wdFindStop
.MatchWildcards = False
.Text = "<<hd^#>>"

Do While .Execute
oRg1.Select
Set oRg2 = Selection.Bookmarks("\line").Range
' place oRg1 at end of line for next pass
oRg1.Start = oRg2.End
oRg1.End = oRg2.End

' move start of oRg2 after tag
oRg2.MoveStartUntil cset:=">"
oRg2.MoveStart unit:=wdCharacter, Count:=2

' handle 1st word
tempstr = Trim(oRg2.Words(1).Text)
tempstr = Right(tempstr, Len(tempstr) - 1)
If AnyCaps(tempstr) Then
oRg2.Words(1).Text = _
"[" & Trim(oRg2.Words(1).Text) & "] "
End If
oRg2.MoveStart unit:=wdWord, Count:=1

' handle remaining words with wildcard replace
With oRg2.Find
.ClearFormatting
.Replacement.ClearFormatting
.Format = False
.Forward = True
.Wrap = wdFindStop
.MatchWildcards = True
.Text = "(<[A-Z][a-z]{1,}>)"
.Replacement.Text = "[\1]"
.Execute Replace:=wdReplaceAll
End With
Loop
End With

Selection.HomeKey unit:=wdStory
End Sub

Private Function AnyCaps(test As String) As Boolean
Dim bRes As Boolean
Dim n As Integer
bRes = False
For n = 1 To Len(test)
If Mid(test, n, 1) = UCase(Mid(test, n, 1)) Then
bRes = True
Exit For
End If
Next n
AnyCaps = bRes
End Function


Rodney Atkins said:
I have SGML-like documents in which the headings are coded <<hd#>>,
where # is a number:

<<hd3>>This is my heading

I need to

--find each head (<<hd#>>) in the doc
--if any "word" in that head has a capital letter, surround it with
brackets

Before:
<<hd1>>This is my Heading with a Cap in it

After:
<<hd1>>This is my
with a [Cap] in it

It's not necessary for the first word unless there's a cap in the
middle.

<<hd1>>[DelMonte] packages peaches

I'm not quite sure where to begin. I can search for "<<hd" and use
Selection.Extend, or perhaps Selection.MoveEnd, but once I have the
heading selected, I'm at a bit of a loss as to how to test it.

Thanks.​
 
P

Peter Hewett

Hi Jay

Yeah, you're not the only one. The next day; I look at some of the stuff I've
posted late at night and think "shite - did I really post that!"

Keep it up + Cheers - Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top