21
$\begingroup$

The following examples only work for the first character of the whole string, but not of words.

s = "words are lowercase";
StringJoin[MapAt[ToUpperCase, Characters[s], 1]]   (* ==> "Words are lowercase" *)
StringReplacePart[#, ToUpperCase[StringTake[#, 1]], 1]&@s  (* ==> "Words are lowercase" *)

Is there any method along these lines to produce correct title case strings of the form:

"Words Are Lowercase"
$\endgroup$
14
  • 5
    $\begingroup$ See the first example in WordBoundary. $\endgroup$
    – Michael E2
    Commented Sep 10, 2013 at 13:24
  • $\begingroup$ @Kuba ah, of course, there are many many words to capitalize. $\endgroup$ Commented Sep 10, 2013 at 13:24
  • $\begingroup$ @MichaelE2 Ah, I think of one example in Help page. $\endgroup$ Commented Sep 10, 2013 at 13:25
  • 4
    $\begingroup$ To the close-voters, how exactly is this easily found in the documentation? What did you search by, or by which path did you navigate to this example? $\endgroup$
    – Mr.Wizard
    Commented Sep 10, 2013 at 13:36
  • 4
    $\begingroup$ I didn't think it should be closed, but I looked up "WordBoundary" just to check on using it to make up an answer. However, if you search for "capitalize", WordBoundary is the second hit in V9. But see István's answer - those sorts of unexpected complications are reasons such questions should not be closed, at least not right away. $\endgroup$
    – Michael E2
    Commented Sep 10, 2013 at 13:54

7 Answers 7

27
$\begingroup$

From the documentation, though IMHO not easy to find:

StringReplace["this is a test", WordBoundary ~~ x_ :> ToUpperCase[x]]
"This Is A Test"

István Zachar highlighted a problem with WordBoundary that I'm still trying to understand. Nevertheless it seems that one can use:

strAcc = "árv ízt űr őt ük örf úr óg ép";

StringReplace[strAcc, z : (StartOfString | WhitespaceCharacter ~~ _) :> ToUpperCase[z]]
"Árv Ízt Űr Őt Ük Örf Úr Óg Ép"

It appears that the PCRE library at least as used by Mathematica does not recognize certain characters as letters. A few examples:

StringReplace[strAcc, z : RegularExpression["(?:\\A|\\s)."] :> ToUpperCase[z]]
StringReplace[strAcc, z : RegularExpression["\\b."] :> ToUpperCase[z]]
"Árv Ízt Űr Őt Ük Örf Úr Óg Ép"

"Árv Ízt űR őT Ük Örf Úr Óg Ép"  (* note odd handling *)
StringCases["abcőű", RegularExpression["\\w"]]
{"a", "b", "c"}                  (* ő and ű missing *)
$\endgroup$
6
  • 1
    $\begingroup$ Actually, LetterQ /@ Characters@"áéíóöőüű" returns all True, so it is only LetterCharacter that does not consider them letters... $\endgroup$ Commented Sep 10, 2013 at 13:58
  • $\begingroup$ @IstvánZachar Thank you. I'm guessing this has something to do with compilation to PCRE. Do you happen to know if Perl considers these to be letters? $\endgroup$
    – Mr.Wizard
    Commented Sep 10, 2013 at 14:00
  • $\begingroup$ Sorry, no idea about Perl. $\endgroup$ Commented Sep 10, 2013 at 14:05
  • $\begingroup$ This isn't a separate answer, but it's worth knowing that "Title case" usually does not capitalize certain small words, so you'd do something like: replacelittlewords = z : (StartOfString | WhitespaceCharacter ~~ # ~~ WhitespaceCharacter) :> ToLowerCase[z] & /@ {"A", "An", "The", "Is", "On", "To"} and then StringReplace[ StringReplace["This is a test about which you will be amazed", z : (StartOfString | WhitespaceCharacter ~~ _) :> ToUpperCase[z]], replacelittlewords]. $\endgroup$
    – Verbeia
    Commented Sep 11, 2013 at 3:18
  • 1
    $\begingroup$ Note that at least in version 11.3 RegularExpression["\\b."] correctly recognizes "ű" and "ő" as letters (but in version 8.0.4 it doesn't). $\endgroup$ Commented Nov 2, 2018 at 7:06
19
$\begingroup$

Actually, WordBoundary won't always work correctly (see this thread):

str = "the lazy dog jumped over the quick brown fox.";
strAcc = "árv ízt űr őt ük örf úr óg ép";

StringReplace[str, WordBoundary ~~ x_ :> ToUpperCase[x]]
StringReplace[strAcc, WordBoundary ~~ x_ :> ToUpperCase[x]]
"The Lazy Dog Jumped Over The Quick Brown Fox."

"Árv Ízt űR őT Ük Örf Úr Óg Ép"   (* note ű,ő instead of Ű,Ő *)

Use instead this custom made toTitleCase:

toTitleCase[str__] := StringJoin@Riffle[ToUpperCase@StringTake[#, 1] <> 
       ToLowerCase@StringTake[#, {2, -1}] & /@ StringSplit@StringJoin@str, " "];

toTitleCase[str]
toTitleCase[strAcc]
"The Lazy Dog Jumped On The Quick Brown Fox."

"Árv Ízt Űr Őt Ük Örf Úr Óg Ép"

In version 10.1, this is built in as ToTitleCase (if I recall correctly, it was an experimental function; documentation is no longer accessible on the net). This version removes all non-alphanumeric characters from the string; I assume this is a bug.

ToTitleCase[str]
ToTitleCase[strAcc]
"The Lazy Dog Jumped On The Quick Brown Fox"

"Árv Ízt Űr Őt Ük Örf Úr Óg Ép"

ToTitleCase has an eventful development history: in version 11, it is removed from among the built-ins, but is still acccessible from the "GeneralUtilities`" context. It does not remove non-alphanumeric characters anymore from the string.

GeneralUtilities`ToTitleCase[str]
GeneralUtilities`ToTitleCase[strAcc]
"The Lazy Dog Jumped Over the Quick Brown Fox."

"Árv Ízt Űr Őt Ük Örf Úr Óg Ép"
$\endgroup$
5
  • 2
    $\begingroup$ Jumped on the fox? We learned it as over. :o) $\endgroup$
    – Mr.Wizard
    Commented Sep 10, 2013 at 13:39
  • $\begingroup$ @Mr.Wizard Well, obviously there were other messages sent over the Moscow-Budapest hotline... If you examine it more closely, there are further discrepancies :) (simple explanation: my memory failed me) $\endgroup$ Commented Sep 10, 2013 at 13:49
  • $\begingroup$ Do you understand why ű is not considered a LetterCharacter? $\endgroup$
    – Mr.Wizard
    Commented Sep 10, 2013 at 13:51
  • $\begingroup$ The function ToTitleCase doesn't seem to exist anymore (v11). $\endgroup$
    – a06e
    Commented Apr 4, 2017 at 20:21
  • 1
    $\begingroup$ @becko Thanks for pointing it out! Actually it does exist, but in GeneralUtilities; please see edit. $\endgroup$ Commented Apr 5, 2017 at 7:16
7
$\begingroup$

While I endorse Mr.Wizard's pattern matching solution and I've given Istvàn +1 I would also like to submit this function which is meant to not rely on string patterns and be as readable as possible:

toTitleCase[str_] := StringJoin[
  MapAt[
   ToUpperCase, Characters[str], 
   Position[Characters[" " <> StringTrim@str], " "]
   ]
  ]

toTitleCase["the lazy dog jumped over the quick brown fox"]

"The Lazy Dog Jumped Over The Quick Brown Fox"

$\endgroup$
2
  • $\begingroup$ I'm sorry, but I can't endorse this. MapAt has poor algorithmic complexity, so you are shifting an operation out of an optimized domain into a known inefficient one. You have also introduced a bug in the case that there is a trailing space on the string. $\endgroup$
    – Mr.Wizard
    Commented Sep 10, 2013 at 18:48
  • $\begingroup$ @Mr.Wizard Fair enough, I didn't think about it in terms of speed especially with the sample being so tiny. :/ Fixed the problem with the trailing space though. $\endgroup$
    – C. E.
    Commented Sep 10, 2013 at 19:17
5
$\begingroup$

Starting in M11.2 or M11.3 you can use Capitalize with "TitleCase" as the second argument:

Capitalize["words are lowercase", "TitleCase"]

"Words Are Lowercase"

$\endgroup$
0
2
$\begingroup$

I just came across this problem with strings containing parentheses and acronyms, and the above solutions didn't work correctly for me. This is the solution I came up with:

toTitleCase[str_] := StringReplace[
    ToUpperCase@str, (f_?LetterQ ~~ rest:(LetterCharacter..)) :> 
    f <> ToLowerCase[rest]
];

toTitleCase["the (LAZY) d.O.G."]

"The (Lazy) D.O.G."

$\endgroup$
1
$\begingroup$
rule = RegularExpression["\\b([[:lower:]])"] :> ToUpperCase["$1"];
StringReplace[s, rule]
$\endgroup$
7
  • $\begingroup$ It is worth to mention that your code works correctly in version 11.3, but not in version 8.0.4. (+1) $\endgroup$ Commented Nov 2, 2018 at 7:08
  • $\begingroup$ @AlexeyPopkov Then what would be wrong in Version 8.0.4? $\endgroup$ Commented Nov 2, 2018 at 7:46
  • $\begingroup$ It doesn't recognize "ű" and "ő" as letters, see the answer by Mr.Wizard: mathematica.stackexchange.com/a/32045/280 $\endgroup$ Commented Nov 2, 2018 at 7:50
  • $\begingroup$ @AlexeyPopkov Might "[[:lower:]]" have a narrower scope in that version? $\endgroup$ Commented Nov 2, 2018 at 7:52
  • $\begingroup$ \\b certainly has narrower scope. $\endgroup$ Commented Nov 2, 2018 at 7:55
0
$\begingroup$

All of the answers posted has a wrong result when applied to this string :

"hoSsEiN HAdi" -> "HoSsEiN HAdi"

But you can use the below code to ensure that all of the subsequent letters has been changed to LowerCase:

StringJoin @ StringCases["aA AA aa Aa 1a 2a a2 A2", WordBoundary~~Shortest[x_] ~~ Shortest[y___] ~~ WordBoundary :> ToUpperCase[x] <> ToLowerCase[y]]

This gives the following result :

"Aa Aa Aa Aa 1a 2a A2 A2"
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.