Force Capitalized String

I would like to capitalize the first letter of each word, but I haven’t found a way to do that.
As it is not a predefined text, would I have to create a function for this?
I only saw the UPPER and LOWER.
Any idea?

You’ll need to write a function/procedure to do this. Shouldn’t be that hard.
StringTheory does have a method Captitalze, if you already have that.

There is the CAP attribute you can put on an entry field. In the dictionary it is “Word Capitalized” on the Attributes page. It will capitalize the first word and any subsequent words after a space as you type. It doesn’t guarantee that the user won’t do something unexpected, like type in two words together and then add a space between them, but it will work 99% of the time.

Jon, also if you copy/paste something like “the quick fox jumped over the lazy sleeping dog.” into the entry field the CAP functionality doesn’t get applied.

I found this code.

UpLowText  PROCEDURE (*string Txt)
Ndx  long,auto
  CODE
    if ~Txt then return.
    if SIZE(Txt)<3   |                              !Edit to prevent Txt[3] error,
    or Txt[2] >= 'a' or Txt[3] >= 'a' then return.  !<---NOTE: Do not touch if already some lowercase

    Txt = lower(Txt)                 !Lower it all and then Upper the right letters
    Txt[1] = UPPER(Txt[1])           !1st letter UPPER
    loop Ndx = 2 to len(clip(txt))
         Case Txt[ Ndx - 1 ]
         of 'a' to 'z'
         of 'A' to 'Z'
         of '0' to '9'
                  !Prior char is Letter/Number so leave Current as lower
         else
             Txt[Ndx] = UPPER(Txt[Ndx])   !Prior Not letter/number so UPPER Current Char
         end
    end
    return

You’ll have to adjust to the rules you prefer. Often only the Letter after a Space is Upper’d. This code does upper after all special characters so after apostrophe is upper e.g. O’Donnell not O’donnell

Edit:
Fixed to check for IF SIZE()<3 because Txt[3] >= 'a' would be an illegal array subscript.
This was meant for strings longer than 3 bytes like Name and Address. Now a 2 byte size string does not get UpLow which I’m fine with.
Thanks @vitesse

3 Likes

I know it is not your code Carl but just be aware that this will fail if Txt is only one or two characters long.

need to either check the size(Txt) or use sub() instead.

I think Mike Hanson had a “proper case” function floating around somewhere that catered for lots of special cases. I will see if I can find it.

1 Like

from a message by Mike Hanson 17 May 2003, 03:43:23

on this thread:
https://groups.google.com/g/comp.lang.clarion/c/pHcdA5kUm4Y/m/j7WwwKN-4iAJ

Here's the best of the bunch, evolved from DOS days, and now availabe
in the BoxSoft SuperStuff templates.  Not only does it handle names
like McMaster and O'Henry, it will also handle McDonald's without
capitalizing the trailing "s".  It's aware of international characters
too.
-=> Mike Hanson <=-


  MEMBER
!==============================================================================
  MAP
Proper               PROCEDURE(STRING N),STRING
IsNumericChar        PROCEDURE(STRING pChar),BYTE
IsAlphaChar          PROCEDURE(STRING pChar),BYTE
IsLowerChar          PROCEDURE(STRING pChar),BYTE
IsUpperChar          PROCEDURE(STRING pChar),BYTE
ToLower              PROCEDURE(STRING pChar),STRING
ToUpper              PROCEDURE(STRING pChar),STRING
  END!MAP
!==============================================================================
Mod:IntU             STRING('ЗДЕЙЖЦЬС'),STATIC
Mod:IntL             STRING('здейжцьс'),STATIC
Mod:IntLC            STRING('ьйвдаезклипомжфцтыщябнуъс'),STATIC
Mod:IntUC            STRING('ЬЙAДAЕЗEEEIIIЖOЦOUUYAIOUС'),STATIC
!==============================================================================
Proper PROCEDURE(STRING N)
Loc:RetVal       STRING(255)
Loc:Char         STRING(1),DIM(255),OVER(Loc:RetVal)
Loc:X            BYTE
Loc:Start        BYTE
Loc:Len          BYTE
Loc:Mc           BYTE(-1)
  CODE
  Loc:RetVal = N
  IF IsLowerChar(Loc:Char[1])
    Loc:Char[1] = ToUpper(Loc:Char[1])
  END!IF
  IF Loc:Char[1] = 'M' AND UPPER(Loc:Char[2]) = 'C'
    Loc:Char[2] = 'c'
    Loc:Start   = 3
    Loc:Mc      = 1
  ELSE
    Loc:Start = 2
  END!IF
  Loc:Len = LEN(CLIP(N))
  LOOP Loc:X = Loc:Start TO Loc:Len
    IF IsAlphaChar(Loc:Char[Loc:X])
      IF IsAlphaChar(Loc:Char[Loc:X-1])
        IF Loc:Mc <> Loc:X-2
          IF IsUpperChar(Loc:Char[Loc:X])
            Loc:Char[Loc:X] = ToLower(Loc:Char[Loc:X])
          END!IF
          IF Loc:Char[Loc:X]='c' AND Loc:Char[Loc:X-1]='M'
            Loc:Mc = Loc:X - 1
          END!IF
        ELSE
          IF IsLowerChar(Loc:Char[Loc:X])
            Loc:Char[Loc:X] = ToUpper(Loc:Char[Loc:X])
          END!IF
        END!IF
      ELSIF UPPER(Loc:Char[Loc:X])='S' AND Loc:Char[Loc:X-1]=''''
        IF Loc:X >= 3
          IF Loc:Char[Loc:X-2] = 'O'
            Loc:Char[Loc:X] = 'S'
          ELSE
            Loc:Char[Loc:X] = 's'
          END!IF
        ELSE
          Loc:Char[Loc:X] = 's'
        END!IF
      ELSIF IsNumericChar(Loc:Char[Loc:X-1])
        IF IsUpperChar(Loc:Char[Loc:X])
          Loc:Char[Loc:X] = ToLower(Loc:Char[Loc:X])
        END!IF
      ELSIF IsLowerChar(Loc:Char[Loc:X])
        Loc:Char[Loc:X] = ToUpper(Loc:Char[Loc:X])
      END!IF
    END!IF
  END!LOOP
  RETURN Loc:RetVal
!==============================================================================
IsNumericChar PROCEDURE(STRING pChar)
  CODE
  RETURN CHOOSE(pChar[1] >= '0' AND pChar[1] <= '9')
!==============================================================================
IsAlphaChar PROCEDURE(STRING pChar)
  CODE
  IF (pChar[1] >= 'A' AND pChar[1] <= 'Z')  |
  OR (pChar[1] >= 'a' AND pChar[1] <= 'z')  |
  OR INSTRING(pChar[1], Mod:IntU)           |
  OR INSTRING(pChar[1], Mod:IntLC)
    RETURN 1
  ELSE
    RETURN 0
  END!IF
!==============================================================================
IsLowerChar PROCEDURE(STRING pChar)
  CODE
  IF pChar[1] >= 'a' and pChar[1] <= 'z'  |
  OR INSTRING(pChar[1], Mod:IntLC)
    RETURN 1
  ELSE
    RETURN 0
  END!IF
!==============================================================================
IsUpperChar PROCEDURE(STRING pChar)
  CODE
  IF pChar[1] >= 'A' and pChar[1] <= 'Z'  |
  OR INSTRING(pChar[1], Mod:IntU, 1)
    RETURN 1
  ELSE
    RETURN 0
  END!IF
!==============================================================================
ToLower PROCEDURE(STRING pChar)
P BYTE,AUTO
  CODE
  IF pChar[1] < '~'
    RETURN  CHR(VAL(pChar)+32)
  ELSE
    P = INSTRING(pChar[1], Mod:IntU)
    RETURN CHOOSE(~P, pChar[1], Mod:IntL[P])
  END!IF
!==============================================================================
ToUpper PROCEDURE(STRING pChar)
P BYTE,AUTO
  CODE
  IF pChar[1] < '~'
    RETURN CHR(VAL(pChar[1])-32)
  ELSE
    P = INSTRING(pChar[1], Mod:IntLC)
    RETURN CHOOSE(~P, pChar[1], Mod:IntUC[P])
  END!IF
!==============================================================================
www.boxsoft.net

also Mike did an article on a later version in Clarion Magazine:

Formatting Names Using Proper Case
by Mike Hanson
Published 2008-12-22

attached here with thanks to Dave Harms.

cmag-2008-12.pdf (793.0 KB)
v10n12proper.zip (23.9 KB)

1 Like

Hi,

Capesoft’s StringTheory has a method called Capitalize that does the job (and a whole lot more)
StringTheory is the template I use the most in every app I write.
I don’t know your situation but I think USD 79.00 is a bargain for what you get.

Cheers,
René

2 Likes

Hello, in the SystemString Class there is the “Capitalize” method. I haven’t tried it, but it’s there.

Just look in LibSrc at SystemString.clw source which shows it does UPPER on the First and UPPER after each Space. So it assumes lower case.

SystemStringClass.ToCapitalize PROCEDURE(STRING pStr)
idx        LONG,AUTO
sLength    LONG,AUTO
 CODE
    idx = 1
    sLength = LEN(pStr)
    IF sLength > 0
       pStr[idx] = UPPER(pStr[idx])     !1st Letter is always UPPER since idx=1
       IF sLength > 1
          LOOP idx = 2 to sLength       !2nd Letter to Length
               IF pStr[idx-1] = ' ' THEN        !IF Previous Char was Space
                  pStr[idx] = UPPER(pStr[idx])  !then Letter is UPPER
               END
          END
       END
    END
    RETURN pStr

Code could be a bit simpler. There is no need to set IDX=1. Since not using CLIP() can change LEN() to SIZE() as simpler.

  CODE
    sLength = SIZE(pStr)
    IF sLength > 0
       pStr[1] = UPPER(pStr[1])     !1st Letter is always UPPER
       IF sLength > 1
... as before ...
1 Like

Glad you find ST useful René - it is certainly my favourite third party tool too as I have mentioned on plenty of other threads and I have spent years working on it.

Rick mentioned it right near the top of this thread.

The reason I mentioned Mike’s versions is that they are more sophisticated and while ST does handle more than just a space as the word delimiter (and allows you to override the default delimiter character list should you wish), it would not handle something like “McDonald”. Mike’s later class in the ClarionMag article handles even more special cases. Of course the various “exceptions” could vary depending on your native language so you may need to salt to taste.

It is also worth looking at various other people’s ideas on that thread I posted earlier:
https://groups.google.com/g/comp.lang.clarion/c/pHcdA5kUm4Y/m/ULt2QV72wVsJ
(although the formatting of the indenting was all lost somewhere in the transfer from the newsgroups).