PDA

View Full Version : [SOLVED:] Check for end of sentence and add a full stop if missing



drhowells
07-24-2012, 07:41 AM
Hi,

I check a lot of large documents and it is amazing how many people still don't put full stops at the end of a sentence.

I want to make a macro which will search a document for the end of a paragraph, see if there is a valid end of sentence character e.g. full stop, question mark etc and if not, automatically insert a full stop.

Any ideas where to start?

Thanks in advance for any help....

fumei
07-24-2012, 04:57 PM
I check a lot of large documents and it is amazing how many people still don't put full stops at the end of a sentence.


Are you saying these large documents are ONE paragraph, in that by full stop you mean a paragraph mark? They are using a Shift-Enter to make a new line (but the same paragraph)?

If so, then by definition you can not have a maco searching for "the end of a paragraph", because without a "full stop" (paragraph mark), there is no end of the paragraph.

Please clarify.

Have you tried recording such a macro? To replace line breaks, with paragraph marks, it wouldf be something like: Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "^l"
.Replacement.Text = "^p"
.Forward = True
.Wrap = wdFindContinue

End With
Selection.Find.Execute Replace:=wdReplaceAll

gmaxey
07-24-2012, 05:26 PM
It is also amazing that Word routinely mistakes abbreviations for sentence stops. See: http://gregmaxey.mvps.org/word_tip_pages/deduced_sentences.html

macropod
07-24-2012, 05:42 PM
Try:

Sub Demo()
Dim Para As Paragraph
For Each Para In ActiveDocument.Paragraphs
If Len(Para.Range.Text) > 1 Then
If Not Para.Range.Characters.Last.Previous Like "[.!?:;]" Then
Para.Range.Characters.Last.InsertBefore "."
End If
End If
Next
End Sub

drhowells
07-25-2012, 01:22 AM
Macropod - you are brilliant. So simple, but so far seems to do the job perfectly. Thank you very much :)

gmaxey - cheers for the link. I had a quick look, but I will need to spend some time looking at it properly later on :)

fumei - Thanks for your reponse. They are large documents with multiple paragraphs.

drhowells
07-25-2012, 02:15 AM
OK, I am going to be really cheeky now :)

Whilst the code does work on simple text in a document, it also adds a full stop to headings, graphics etc. It also appears to be erroring when it hits the end of the first line of a table with a

"Run-time error '5251':
This is not a valid action for the end of a row."

Is there anyway we can adjust the code above so that it will only apply to text in the "Normal" style? I thought this might be the easiest way to eliminate headings, graphics etc.... Or can anyone think of a better idea?

macropod
07-25-2012, 02:31 AM
On the assumption that your in-line graphics are the only things in their paragraphs, you could try something along the lines of:

Sub Demo()
Dim Para As Paragraph
For Each Para In ActiveDocument.Paragraphs
If Len(Para.Range.Text) > 2 And Left(Para.Range.Style.NameLocal, 7) <> "Heading" Then
If Not Para.Range.Characters.Last.Previous Like "[.!?:;]" Then
Para.Range.Characters.Last.InsertBefore "."
End If
End If
Next
End Sub
The above takes care of the table issue too.

drhowells
07-25-2012, 03:25 AM
Thanks again :)

I have tweaked it a bit to include a few other styles that we would want excluded, so that is great and really helpful.

Is there anyway that we can exclude tables altogether from the code? At the moment it is putting a full stop in all of the table headings, every column (when usually we don't want them) etc.

macropod
07-25-2012, 03:31 AM
You could use a test like:
If Len(.Text) > 2 And .Information(wdWithInTable) = False And Left(.Style.NameLocal, 7) <> "Heading" Then

drhowells
07-25-2012, 07:10 AM
This is all brilliant. I seem to have 1 final problem. I keep getting a

"Run-time error '91':
Object variable or With block variable not set"

when I run it. I think it is failing on the Table of Contents. I have tried to exclude the TOC style, but it doesn't seem to make a difference.

At the moment, the code is running on the ActiveDocument. Is there any way I can change it so that it either starts on a certain page e.g. 6 onwards or starts in the second section onwards?

As a side note, some of my users have been creating headings in normal text and then making them look like the headings style (people just don't get styles). This code puts a full stop after these, so it is easy to see which headings contain errors.....great little spin off effect :)

macropod
07-25-2012, 07:36 AM
There seems to be a measure of scope creep going on here. Try the following. I've added some efficiency enhancements:

Sub Demo()
Application.ScreenUpdating = False
Dim Para As Paragraph
On Error Resume Next
For Each Para In ActiveDocument.Paragraphs
With Para.Range
If .Characters.Last.Previous.InRange(ActiveDocument.TablesOfContents(1).Range) = False Then
If Left(.Style.NameLocal, 7) <> "Heading" Then
If Len(.Text) > 2 Then
If Not .Characters.Last.Previous Like "[.!?:;]" Then
.Characters.Last.InsertBefore "."
End If
End If
End If
End If
End With
Next
Application.ScreenUpdating = True
End Sub

drhowells
07-25-2012, 08:07 AM
It all works great! I just added the table check and it does the job. I very much appreciate your help and time on this. Thank you.

jbarber
03-24-2014, 03:40 PM
Hi Dr Howells, I'm trying to achieve what you have here but am unfamiliar with VBA, would you be able to post your solution here? Fundamentally I too need to scan a document and add a fullstop at the end of a paragraph of text without affecting another part of the document nor any other stsyele - in fact the paragraphs are all in a special style, "StandardCo" I'd created. If anyone else can advise, please do, there's a lot of knowledgable experts here.
Thanks
James

macropod
03-24-2014, 04:04 PM
The 'Dr Howells' solution is in post 11. Try:

Sub Demo1()
Application.ScreenUpdating = False
Dim i As Long
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "^p"
.Style = "StandardCo"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = True
.MatchWildcards = False
.Execute
End With
Do While .Find.Found
If Len(Trim(.Duplicate.Paragraphs(1).Range.Text)) > 1 Then
If Not .Characters.Last.Previous Like "[.!?:;]" Then
.Characters.Last.InsertBefore "."
i = i + 1
End If
End If
If .Duplicate.End = ActiveDocument.Range.End Then Exit Do
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
MsgBox i & " instances found & updated."
End Sub
or:

Sub Demo2()
Application.ScreenUpdating = False
Dim Para As Paragraph, i As Long
On Error Resume Next
For Each Para In ActiveDocument.Paragraphs
With Para.Range
If .Style.NameLocal = "StandardCo" Then
If Len(Trim(.Text)) > 2 Then
If Not .Characters.Last.Previous Like "[.!?:;]" Then
i = i + 1
.Characters.Last.InsertBefore "."
End If
End If
End If
End With
Next
Application.ScreenUpdating = True
MsgBox i & " instances found & updated."
End Sub
The first one will be faster, but won't get content terminated by table end-of-cell markers.

iwonder
07-02-2017, 09:03 AM
Hi !
I'm glad to have found this code (Demo2) !!!
I adapted it to do the job for several styles in the document.
But now, I'd like the user be asked (VbYesNOCancel) if he wants the full stop or not for each location where there is none and I tried many many things without any result :banghead:
I'm just a coder monkey...
If someone can help me, it would be great !!!

gmaxey
07-02-2017, 12:47 PM
Sub Demo2()
Dim oPar As Paragraph, lngInst As Long, lngUpdate As Long
On Error Resume Next
For Each oPar In ActiveDocument.Paragraphs
With oPar.Range
If .Style.NameLocal = "Normal" Then
If Len(Trim(.Text)) > 2 Then
If Not .Characters.Last.Previous Like "[.!?:;]" Then
.Characters.Last.Previous.Select
lngInst = lngInst + 1
Select Case MsgBox("Do you want a period here?", vbYesNoCancel, "Add Stop")
Case vbYes
.Characters.Last.InsertBefore "."
lngUpdate = lngUpdate + 1
Case vbNo
Case Else: Exit For
End Select
End If
End If
End If
End With
Next
MsgBox lngInst & " instances found " & lngUpdate & " instances updated."
End Sub

iwonder
07-02-2017, 03:23 PM
Thanks Greg !
I'm silly, I didn't try" Select case MsgBox"... only tried "If MsgBox"...
I'll closely compare the 2 versions of Demo2 and for sure I'll learn new things ! :yes
Thanks again for sharing your knowledge !!!!

Bernard

ShaunW
01-15-2021, 06:51 AM
I know this is a very old thread, but I have found it extremely helpful and so didn't want to start a new thread.

I just wondered, for the code in Demo2 - is there a way to start it from a specific page onwards. I would like to achieve exactly what it achieves, but only from page 8 onwards and ignore pages 1-7 completely.

gmaxey
01-15-2021, 08:40 AM
If you can physically put the cursor at the top of page 8 then Demo3 should work. Otherwise, use Demo4


Sub Demo3()
Dim oRng As Range
Dim oPar As Paragraph, lngInst As Long, lngUpdate As Long
On Error Resume Next
Set oRng = ActiveDocument.Range
oRng.Start = Selection.Range.Start
For Each oPar In oRng.Paragraphs
With oPar.Range
If .Style.NameLocal = "Normal" Then
If Len(Trim(.Text)) > 2 Then
If Not .Characters.Last.Previous Like "[.!?:;]" Then
.Characters.Last.Previous.Select
lngInst = lngInst + 1
Select Case MsgBox("Do you want a period here?", vbYesNoCancel, "Add Stop")
Case vbYes
.Characters.Last.InsertBefore "."
lngUpdate = lngUpdate + 1
Case vbNo
Case Else: Exit For
End Select
End If
End If
End If
End With
Next
MsgBox lngInst & " instances found " & lngUpdate & " instances updated."
End Sub
Sub Demo4()
Dim oRng As Range
Dim oPar As Paragraph, lngInst As Long, lngUpdate As Long
On Error Resume Next
Set oRng = ActiveDocument.Range
oRng.Collapse wdCollapseStart
Do Until oRng.Paragraphs(1).Range.Characters.Last.Information(wdActiveEndPageNumber) = 8
oRng.Move wdParagraph, 1
Loop
oRng.Select
oRng.End = ActiveDocument.Range.End
For Each oPar In oRng.Paragraphs
With oPar.Range
If .Style.NameLocal = "Normal" Then
If Len(Trim(.Text)) > 2 Then
If Not .Characters.Last.Previous Like "[.!?:;]" Then
.Characters.Last.Previous.Select
lngInst = lngInst + 1
Select Case MsgBox("Do you want a period here?", vbYesNoCancel, "Add Stop")
Case vbYes
.Characters.Last.InsertBefore "."
lngUpdate = lngUpdate + 1
Case vbNo
Case Else: Exit For
End Select
End If
End If
End If
End With
Next
MsgBox lngInst & " instances found " & lngUpdate & " instances updated."
End Sub

Walentyn
02-02-2021, 01:02 PM
How would you represent a long dash in that list of excluded characters?

I've tried everything I can think of: copying a long dash into the sequence, adding a forward slash in front of it, putting in character codes Chr(151), ChrW(&H2014). But the macro still finds every paragraph that legitimately ends in a long dash. Even concatenating it as follows:

If Not .Characters.Last.Previous Like "[0123456789'….,!?:*;"")" & "ChrW(&H2014)]"

Stumped!

gmaxey
02-02-2021, 02:25 PM
Do you mean an Em dash? That has a character code of 151

If Not .Characters.Last.Previous Like "[.!?:;" & Chr(151) & "]" Then

Walentyn
02-02-2021, 02:38 PM
Yes, em dash. Wasn't too sure about concatenating in that circumstance, so that helps too.

macropod
02-02-2021, 04:06 PM
You would need to test for any of:
Hyphen - ASCII 45
En-dash - ASCII 150
Em-dash - ASCII 151
Minus sign - ASCII 173
all of which can be inserted explicitly into the string without the need for Chr or ChrW

Walentyn
02-02-2021, 04:23 PM
Fab, thanks. All these numbers can get confusing! ANSI, hex, Unicode ...