Solved Probably an OLD question about Proper Case

mib1019

Member
Local time
Today, 12:28
Joined
Jun 19, 2020
Messages
88
Hello, all.

Is there a function out there for changing a field entry to proper case in the after update event, that traps and corrects problems like this:
Washington DC becomes Washington Dc
McGurley becomes Mcgurley
1125 NW becomes 1124 Nw
etc.

Thanks in advance!
MIB1019
 
Only a specially built Function.

You could get it to ignore strings of less than 3 characters or that ended with a full stop, but that would possibly leave ST. instead of converting it to St.
Simply put: if you can define ALL the rules then you can build a function to accommodate them.
 
Washington DC becomes Washington Dc
McGurley becomes Mcgurley
1125 NW becomes 1124 Nw

Did you state that wrong? The correct version of Washington Dc should be Washington DC.

Even if you think the correct version is Washington Dc, there is no built in function that can do it. Further, it would be hard to write one. This is not really a task suited for computers. If I were to write one I would build a function that did 2 things:

1. Look in a table for a list of known incorrect strings and their corrections. This table would include postal codes (Dc/DC, Ia/IA, etc.), directions (Nw/NW, Se/SE, etc.) and other known common errors.

2. Pattern matched. This would catch all the Scotish/Irish names (Mc, Mac, O', etc) and ensure the letter after them was capitalized.

With that said, you are fighting a losing battle, no matter how smart you make your system, there will always be dumb users. What happens when someone types in "Wachingtn Cd". That guy is out there and will use your system.
 
Washington DC becomes Washington Dc
Hi. I just gave it a try...
strconv.PNG

Is that what you meant?
 
If I understood your question correctly, you already have code/function that changes some text to Proper case. By doing so, it has some undesirable results (such as you posted).
If so, it might not be very hard to write a function that:
  1. It would replace your initial proper-case conversion (i.e. wherever you are using that, replace it with this)
  2. Simply go through the entered text letter by letter and make the following determination:
    1. Capitalize every first letter of a word, and de-capitalize every other letter of the word, with the following exceptions:
      1. Don't de-capitalize any letter that belongs to a pair of 2 capitalized letters
      2. Possibly don't de-capitalize any letter in the middle of a word (less sure on this).
Would it be 100%, of course not, but it might be an improvement.
 
The vbProperCase function is designed to capitalise the first letter in each word and lower case all others.
That does indeed lead to unwanted side effects like those in post #1
As already mentioned the only ways of achieving your desired outcomes are to write your own function which manages specific exceptions which you define and possibly list in an exceptions table.
If you do an online search you may find something close to your needs which you can then adapt.
Good luck
 
Last edited:
I had code that would leave the following alone, or correct it if was the reverse. ?
Code:
? mixed_case("Mcgurley")
McGurley
 
I had code that would leave the following alone, or correct it if was the reverse. ?
Code:
? mixed_case("Mcgurley")
McGurley
Any chance you could upload your mixed_case function?
 
Adding specific exceptions gets close but I've come across some variations of names such as Macarthur and MacArthur. Then there are hyphenated names which do not always follow with an upper case letter after the hyphen. With data importation, I set it up for these to be highlighted to the db operator who decides rather than having a hard rule in code.
 
Any chance you could upload your mixed_case function?
Yes, of course, though not mine, just found it on the net with a few googles. :)
I only needed it to clean up surnames for a DB of mine.
Mixed_Case is just the parent function, as you will see.
 

Attachments

Adding specific exceptions gets close but I've come across some variations of names such as Macarthur and MacArthur. Then there are hyphenated names which do not always follow with an upper case letter after the hyphen. With data importation, I set it up for these to be highlighted to the db operator who decides rather than having a hard rule in code.
That was basically my approach as well. For almost every exception there would be an exception to the exception....which of course made it exceptionally difficult to do automatically 😏
 
Thanks Paul (Gasman)
For the benefit of others, this is the mixed_case code he uploaded

Code:
Option Compare Database
Option Explicit

'This code was originally written by Jay Holovacs.
'It is not to be altered or distributed,
'except as part of an application.
'You are free to use it in any application,
'provided the copyright notice is left unchanged.
'
'Code Courtesy of
'Jay Holovacs
'
Public Function mixed_case(str As Variant) As String
'returns modified string, first character of each word us uppercase
'all others lower case
Dim ts As String, ps As Integer, char2 As String
    If IsNull(str) Then
        mixed_case = ""
        Exit Function
    End If
    str = Trim(str) 'added 11/22/98
    If Len(str) = 0 Then
        mixed_case = ""
        Exit Function
    End If
    ts = LCase$(str)
    ps = 1
    ps = first_letter(ts, ps)
    Special_Name ts, 1 'try to fix the beginning
    Mid$(ts, 1) = UCase$(Left$(ts, 1))
    If ps = 0 Then
        mixed_case = ts
        Exit Function
    End If
    While ps <> 0
        If is_roman(ts, ps) = 0 Then 'not roman, apply the other rules
            Special_Name ts, ps
            Mid$(ts, ps) = UCase$(Mid$(ts, ps, 1)) 'capitalize the first letter
        End If
        ps = first_letter(ts, ps)
    Wend
    mixed_case = ts
End Function
Private Sub Special_Name(str As String, ps As Integer)
'expects str to be a lower case string, ps to be the
'start of name to check, returns str modified in place
'modifies the internal character (not the initial)
Dim iLen As Integer

Dim char2 As String
char2 = Mid$(str, ps, 2) 'check for Scots Mc
If (char2 = "mc") And Len(str) > ps + 1 Then '3rd char is CAP
    Mid$(str, ps + 2) = UCase$(Mid$(str, ps + 2, 1))
End If

char2 = Mid$(str, ps, 2) 'check for ff
If (char2 = "ff") And Len(str) > ps + 1 Then 'ff form
    Mid$(str, ps, 2) = LCase$(Mid$(str, ps, 2))
End If

'char2 = Mid$(str, ps + 1, 1) 'check for apostrophe as 2nd char
'If (char2 = "'") Then '3rd char is CAP
'    Mid$(str, ps + 2) = UCase$(Mid$(str, ps + 2, 1))
'End If

' Allow for a ' anywhere and then UCASE the next character
' Added by Paul Steel based on code above

For iLen = ps To Len(str)
    char2 = Mid$(str, iLen, 1) 'check for apostrophe
    If (char2 = "'") Then 'next char is CAP
        Mid$(str, iLen + 1) = UCase$(Mid$(str, iLen + 1, 1))
    End If
Next

Dim char3 As String
char3 = Mid$(str, ps, 3) 'check for scots Mac
If (char3 = "mac") And Len(str) > ps + 1 Then 'Mac form
    Mid$(str, ps + 3) = UCase$(Mid$(str, ps + 3, 1))
End If

Dim char4 As String
char4 = Mid$(str, ps, 4) 'check for Fitz
If (char4 = "fitz") And Len(str) > ps + 1 Then 'Fitz form
    Mid$(str, ps + 4) = UCase$(Mid$(str, ps + 4, 1))
End If

End Sub
Private Function first_letter(str As String, ps As Integer) As Integer
'ps=starting point to search (starts with character AFTER ps)
'returns next first letter, 0 if no more left
'modified 6/18/99 to handle hyphenated names
Dim p2 As Integer, p3 As Integer, s2 As String
    s2 = str
    p2 = InStr(ps, str, " ") 'points to next blank, 0 if no more
    p3 = InStr(ps, str, "-") 'points to next hyphen, 0 if no more
    If p3 <> 0 Then
        If p2 = 0 Then
            p2 = p3
        ElseIf p3 < p2 Then
            p2 = p3
        End If
    End If
    If p2 = 0 Then
        first_letter = 0
        Exit Function
    End If
    'first move to first non blank, non punctuation after blank
    While is_alpha(Mid$(str, p2)) = False
        p2 = p2 + 1
        If p2 > Len(str) Then 'we ran off the end
            first_letter = 0
            Exit Function
        End If
    Wend
    first_letter = p2
End Function
Public Function is_alpha(ch As String)
'returns true if this is alphabetic character
'false if not
    Dim c As Integer
    c = Asc(ch)
    Select Case c
        Case 65 To 90
            is_alpha = True
        Case 97 To 122
            is_alpha = True
        Case Else
            is_alpha = False
    End Select
    
End Function
Private Function is_roman(str As String, ps As Integer) As Integer
'starts at position ps, until end of word. If it appears to be
'a roman numeral, than the entire word is capped in passed back
'string, else no changes made in string
'returns 1 if changes were made, 0 if no change
Dim mx As Integer, p2 As Integer, flag As Integer, i As Integer
    mx = Len(str) 'just so we don't go off the edge
    p2 = InStr(ps, str, " ") 'see if there is another space after this word
    If p2 = 0 Then
        p2 = mx + 1
    End If
    'scan to see if any inappropriate characters in this word
    flag = 0
    For i = ps To p2 - 1
        If InStr("ivxIVX", Mid$(str, i, 1)) = 0 Then
            flag = 1
        End If
    Next i
    If flag Then
        is_roman = 0
        Exit Function 'this is not roman numeral
    End If
    Mid$(str, ps) = UCase$(Mid$(str, ps, p2 - ps))
    is_roman = 1
End Function

Place in a standard module
 
Found this in an older file. You could adapt and include other explicit patterns.
I have seen people named Macdonald and MacDonald and that's the way they spelled their names.
So I doubt there is a standard function that will satisfy all names - especially those Macdonalds that you may consider exceptions.

Code:
Sub testMC()
    Dim testname As String: testname = "O'mally" '"Mcdonald"
    If Left(testname, 2) = "Mc" Or Left(testname, 2) = "O'" Then
        testname = Left(testname, 2) & UCase(Mid(testname, 3, 1)) & Mid(testname, 4)
        Debug.Print testname
    End If
End Sub
 
I've done this kind of thing by applying Proper case then using Replace() in a recapitalize wrapper function that feeds the find and replace strings as parameters. You can loop through the function with various pairs to replace in the code but it is better to supply the pairs in a table and use a query.

Join the replacement pairs table to the data table with InStr() as the criteria so it only runs the replacement function when the search string is present. The pairs table is much easier to maintain when you need to add new pairs rather than changing the function. Selectively concatenate spaces to the front and back of the pairs and the values being searched to recognise the beginning and end of words. The pairs can have another field to indicate if they must be found at the beginning or end of a word (ie to indicate whether to concatenate the space or not).

The names to be fixed are endless. Dutch names like "van der Wakker" and "deGunst" are especially fun for example. Then different individuals handle capitalisation of the same names differently. Virtually impossible to work around.

It is easy to make unintentional changes too. You can deal with this problem by have a table of exceptions related to the pairs table that will cause the function to skip those records.

Alternatively you could build Regular Expression strings based on the pairs table but we have found before that Regular Expressions are very slow.
 

Users who are viewing this thread

Back
Top Bottom