Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Mapping strings, changing case of first letter of string 1

Status
Not open for further replies.

cubicle4

Programmer
Jun 18, 2001
18
US
I have a string that I would like to change the case of only the first letter to upper case. This string can be any word. Anyhow, I am trying to apply string mapping to it to accomplish this. Yeah, sure I could map each character of the alphabet to its corresponding uppercase value but that wouldn't be any fun.

set stringWord morning

proc changeCase {instring} {
set newString [string map "$instring" {[string range $instring 0 0] [string toupper [string range $instring 0 0]]}]
};

set stringWord2 [changeCase $stringWord]

The value being passed to the proc is "morning" with the resulting value of "Morning" being returned.

TIA
Harold
 
Only it looks like you left out the "return $newString" Bob Rashkin
rrashkin@csc.com
 
Actually that was a typo. I do return the $newString.

When I test this in tclpro this is the error that i get.

char map list unbalanced

 
If you're using Tcl 8.1.1 or later, you can simply use the string totitle command, which does exactly what you want to do:

[tt]% string totitle "morning"
Morning
% string totitle "hAvE A GREAT moRNing!"
Have a great morning![/tt]

By the way, string index is a much more efficient way to retrieve a single character from a string:

[tt]string index string index[/tt]

And Tcl 8.1.1 also introduced the string replace command, which allows you to replace a range of characters in a string:

[tt]string replace string first last ?string?[/tt]

For example:

[tt]% set home "I live in San Jose, California"
I live in San Jose, California
% set newhome [ignore][string replace $home 14 17 "Francisco"[ignore]]
I live in San Francisco, California[/tt] - Ken Jones, President
Avia Training and Consulting
866-TCL-HELP (866-825-4357) US Toll free
415-643-8692 Voice
415-643-8697 Fax
 
I forgot to complete what I was saying...

Like I mentioned, string totitle returns a string where the first character is capitalized, and all other characters converted to lower case. (Yes, it should have been called "string tosentence", not "string totitle", but I didn't name the command.) If it's important to you not to change the case of the other letters in the string, all you need to do is combine the commands I described above:

[tt]% set str "this is a TEST!"
this is a TEST!

this is a TEST!
% set newstr [ignore][string replace $str 0 0 [string toupper [string index $str 0]]][/ignore]
This is a TEST![/tt] - Ken Jones, President
Avia Training and Consulting
866-TCL-HELP (866-825-4357) US Toll free
415-643-8692 Voice
415-643-8697 Fax
 
Great, that worked. Unfortunately the book I have did not have the string totitle command. Thanks for the help everyone.
 
I do this all the time. The proper syntax is "string map {map} string". So your code would be [string map {[string range $instring 0 0] [string toupper [string range $instring 0 0]]} $instring] Bob Rashkin
rrashkin@csc.com
 
Good try, Bong, but your attempt still doesn't work. We've got two fundamental problems with this approach, both of which are worth examining.

First is an error of programming logic. string map maps all occurrences of the input patterns with the output patterns. So, this produces an incorrect result if the first character appears elsewhere in the string. A simple example:

[tt]% set str "good morning"
good morning
% string map {g G} $str
Good morninG[/tt]

Thus, string map is simply an inappropriate tool for the task at hand.

Second is an error of Tcl syntax. Let's take a closer look at Bong's example, replacing the string ranges with simpler string indexs:

[tt]% [ignore]string map {[string index $str 0] [string toupper [string index $str 0]]} $str[/ignore]
good morning[/tt]

No characters were translated. That's because we quoted the map list argument with {} characters. This is an easy mistake to make, because it is the Tcl convention to use {} characters to quote a static list argument. However, the {} quoting characters prevent all substitutions from taking place within the argument when Tcl parses the command. Thus, to Tcl it looks as though we've given string map a 10-element mapping list. (Remember, a Tcl list is a whitespace-separated sequence of elements.) Here's a demonstration:

[tt]% set str {$stringify}
$stringify
% string map {[ignore][[/ignore]string index $str 0[ignore]][/ignore] [ignore][string toupper [string index $str 0]]} $str[/ignore]
0]ingify[/tt]

So, your first inclination might be simply to replace the {} quotes with "" quotes. Don't do it! At first, it might seem to function. For example:

[tt]% set str "good morning"
good morning
% [ignore]string map "[string index $str 0] [string toupper [string index $str 0]]" $str[/ignore]
Good morninG[/tt]

But it turns out that we've got a bug just waiting to spring on us:

[tt]% set test2 "{this is {going to break"
{this is {going to break
% [ignore]string map "[string index $test2 0] [string toupper [string index $test2 0]]" $test2[/ignore]
unmatched open brace in list[/tt]

When Tcl performed the substitution in this case, it ended up with "{ {", which it passed to string map as the mapping list. This is not a valid Tcl list, as {} characters are used to quote list elements that contain whitespace characters.

So, what do we do when neither {} nor "" work? Well, the moral of this story is to always use Tcl list commands when building up lists programmatically. They take care of ensuring that the result is a valid Tcl list. In this case, the list command itself is what we want. The following will work in all cases:

[tt]% set str "good morning"
good morning
% string map [ignore][list[/ignore] [ignore][string index $str 0] [string toupper [string index $str 0]][/ignore][ignore]][/ignore] $str
Good morninG
% set str "{this is not {going to break"
{this is not {going to break
% string map [ignore][list[/ignore] [ignore][string index $str 0] [string toupper [string index $str 0]][/ignore][ignore]][/ignore] $str
{this is not {going to break[/tt]

It just doesn't produce the result we wanted, as discussed above. - Ken Jones, President
Avia Training and Consulting
866-TCL-HELP (866-825-4357) US Toll free
415-643-8692 Voice
415-643-8697 Fax
 
AviaTraining.
That's why "you're the man". I had noticed the syntax error and responded before I even tried to perform the actual task. Naturally, it didn't work and it didn't occur to me to use "list". Bob Rashkin
rrashkin@csc.com
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top