Skip to main content
GameDev.net gamedev.net
🔒 Locked

std::string class, is there any built in lowercase converter?

Started by johnnyBravo Aug 11, 2005 at 1:28 AM 16 replies 28.5k views
Original Post
johnnyBravo
johnnyBravo
Hi im using std::string, and i'm doing a string sort and a string search(by dividing and checking if the string im looking for is >, =, < then the marker). Anyway the problem is when sorting capitals with lower case strings, it puts captials in front of lower case, eg: Hello and hi So I was wondering if the std:string class comes with some kind of toLowerCase function? Thanks
Fruny
Fruny
No, it doesn't. The reason is that std::string is locale independent and that different languages (locales) have different conventions regarding such conversions. It is easy to do, though, by running one of the tolower() functions (there are several, which causes some trouble with generic code) over each character. Here is a way for you to do it:

#include <cctype>#include <string>std::string str = "Hello World!";for(int i=0; i<str.size();++i)   str = tolower(str);


It's not the ideal way, but that will do the work. I'm sure somebody will come along shortly to post the transform + locale + functor version [grin]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
load_bitmap_file
load_bitmap_file
Quote:
Original post by Fruny
It's not the ideal way, but that will do the work. I'm sure somebody will come along shortly to post the transform + locale + functor version [grin]


Well, here's a halfway-there version:

std::transform(theString.begin(), theString.end(), theString.begin(), tolower);


Pretty much the same thing as Fruny's except you don't have to manually write the for loop yourself. Doesn't use any locale/functor goodness.

EDIT: Now I'll join Fruny in waiting for someone to post the transform + locale + functor version [grin]
johnnyBravo
johnnyBravo
hmm on the net, it says tolower() arg is an ascii integer, and returns the lower ascii integer.

does the class have some kind of ascii to string converter?

Thanks
silvermace
silvermace
the tolower you are refering to is the C standard library version
look for std::tolower ie, C++ Standard Library
Sneftel
Sneftel
Quote:
Original post by johnnyBravo
hmm on the net, it says tolower() arg is an ascii integer, and returns the lower ascii integer.

does the class have some kind of ascii to string converter?

The ASCII integer is what you want. Read up on what std::transform does; it operates on an element at a time.
MrEvil
MrEvil
Quote:
Original post by load_bitmap_file
Quote:
Original post by Fruny
It's not the ideal way, but that will do the work. I'm sure somebody will come along shortly to post the transform + locale + functor version [grin]


Well, here's a halfway-there version:

std::transform(theString.begin(), theString.end(), theString.begin(), tolower);


Pretty much the same thing as Fruny's except you don't have to manually write the for loop yourself. Doesn't use any locale/functor goodness.

EDIT: Now I'll join Fruny in waiting for someone to post the transform + locale + functor version [grin]


Erm...

#include<functional>#include<string>std::transform(theString.begin(), theString.end(), theString.begin(), std::bind2nd(&std::tolower, locale("fr_FR")) );std::transform(theString.begin(), theString.end(), theString.begin(), std::bind2nd(&std::tolower, locale("POSIX")) );

?

PS. Indeed, Boost String Algorithms are most excellent
snk_kid
snk_kid
Quote:
Original post by load_bitmap_file
EDIT: Now I'll join Fruny in waiting for someone to post the transform + locale + functor version [grin]


#include <boost/bind.hpp> // bind#include <locale>         // tolower#include <algorithm>      // transform#include <string>template < typename ForwardIterator >inline ForwardIteratortolower(ForwardIterator first, ForwardIterator last,		const std::locale& locale_ref = std::locale()) {   using namespace boost;   using namespace std;   typedef typename std::iterator_traits<ForwardIterator>::value_type       value_type;   return transform(first, last, first, 		bind(&std::tolower<value_type>, _1, cref(locale_ref)));}template < typename CharT, typename Traits, typename Alloc >inline std::basic_string<CharT, Traits, Alloc>&tolower(std::basic_string<CharT, Traits, Alloc>& s,		const std::locale& locale_ref = std::locale()) {	   tolower(s.begin(), s.end(), locale_ref);   return s;}
snk_kid
snk_kid
Quote:
Original post by MrEvil
Erm...

#include<functional>#include<string>std::transform(theString.begin(), theString.end(), theString.begin(), std::bind2nd(&std::tolower, locale("fr_FR")) );std::transform(theString.begin(), theString.end(), theString.begin(), std::bind2nd(&std::tolower, locale("POSIX")) );

?


Well that shouldn't compile, apart from missing some headers [grin], std::tolower in the header locale is a template function, you need to do an explicit instantiation when giving the address to std::bind2nd
ReKlipz
ReKlipz
actually, you could do this:

string start = "this iS a StRING";
char tmp[] = start.c_str();
strlwr(tmp);
start = string(tmp);

or something to that effect...
Enigma
Enigma
Quote:
Original post by ReKlipz
actually, you could do this:

string start = "this iS a StRING";
char tmp[] = start.c_str();
strlwr(tmp);
start = string(tmp);

or something to that effect...

Congratulations. That's less correct (char tmp[] = start.c_str(); is illegal), less portable (strlwr() is not standard), less efficient (three memory allocations and two copies) and longer than solutions that have already been posted [lol]

Enigma

Anon Mike
Anon Mike
Just to be pedantic, probably the reason why there's no lowercasing functionality built-in to std::string is that it's actually really hard to do. The "call tolower on each character" algorithms discussed here don't work for anything except western european languages and not even all of those.

I don't know what boost::to_lower does. In Windows you can call LCMapString.
-Mike
snk_kid
snk_kid
Quote:
Original post by Anon Mike
The "call tolower on each character" algorithms discussed here don't work for anything except western european languages and not even all of those.


Either missed my post or i wasted my money on this book [grin]. Notice that there is template function of std::tolower in the C++ header locale that is a internationalized/localized version and independent of character type. I'm pretty sure that it probably just uses the facet std::ctype's tolower member function internally.
mfawcett
mfawcett
Quote:
Original post by snk_kid
Quote:
Original post by Anon Mike
The "call tolower on each character" algorithms discussed here don't work for anything except western european languages and not even all of those.


Either missed my post or i wasted my money on this book [grin]. Notice that there is template function of std::tolower in the C++ header locale that is a internationalized/localized version and independent of character type. I'm pretty sure that it probably just uses the facet std::ctype's tolower member function internally.


I think you're right, but to be sure you could just wrap that yourself.
// In-place conversionstemplate <typename Elem, typename Traits, typename Ax>std::basic_string<Elem, Traits, Ax> &tolower(std::basic_string<Elem, Traits, Ax> *str){	std::use_facet<std::ctype<Elem> >(locale()).tolower(&*str->begin(), &*str->end());	return *str;}// Copy conversionstemplate <typename Elem, typename Traits, typename Ax>std::basic_string<Elem, Traits, Ax> tolower(std::basic_string<Elem, Traits, Ax> str){	std::use_facet<std::ctype<Elem> >(locale()).tolower(&*str.begin(), &*str.end());	return str;}

--Michael Fawcett
Anon Mike
Anon Mike
Quote:
Original post by snk_kid
Quote:
Original post by Anon Mike
The "call tolower on each character" algorithms discussed here don't work for anything except western european languages and not even all of those.


Either missed my post or i wasted my money on this book [grin]. Notice that there is template function of std::tolower in the C++ header locale that is a internationalized/localized version and independent of character type. I'm pretty sure that it probably just uses the facet std::ctype's tolower member function internally.


The fundamental problem is that there is not necessarily a one-to-one mapping between upper and lower case (all this assumes of course that there is even such a thing as case in a particular locale but that's a different issue). The canonical example is some German lower-case letter that maps to "SS" in upper-case. Apparently there are even some languages where the mapping is context-sensitive but I'm not enough of an expert to know for sure. The end result of all this is that to be correct you need to do a whole-string analysis which of course a character-by-character conversion doesn't do.

Your code seems to resolve to call to std::transform which does things character-by-character.

But like I said, I'm being pedantic. If the target is just English then character-by-character will work fine.
-Mike
snk_kid
snk_kid
Quote:
Original post by mfawcett
I think you're right, but to be sure you could just wrap that yourself.


I've got conformation that std::tolower/touppwer is indeed just a convenience function that delegates to std::ctype::tolower/upper.

On side note that code has a slight issue using the range version of std::ctype::toupper/lower, as std::basic_string isn't guaranteed to hold its elements contiguously in memory (although alot of imps still do) so the safest thing to do is to use something like std::transform with std::ctype::touppwer/lower single character version.


Quote:
Original post by Anon Mike
The fundamental problem is that there is not necessarily a one-to-one mapping between upper and lower case (all this assumes of course that there is even such a thing as case in a particular locale but that's a different issue). The canonical example is some German lower-case letter that maps to "SS" in upper-case. Apparently there are even some languages where the mapping is context-sensitive but I'm not enough of an expert to know for sure. The end result of all this is that to be correct you need to do a whole-string analysis which of course a character-by-character conversion doesn't do.

Your code seems to resolve to call to std::transform which does things character-by-character.

But like I said, I'm being pedantic. If the target is just English then character-by-character will work fine.


C++ locale framework can handle localized sensitive contexts and if what your saying is true and you want to go down standard C++ route then you would have to use the range version/overload of std::ctype::tolower/upper.

Also don't forget that these methods could easily be implementated in terms of functions such as LCMapString that you mentioned earlier.
mfawcett
mfawcett
Quote:
Original post by snk_kid
I've got conformation that std::tolower/touppwer is indeed just a convenience function that delegates to std::ctype::tolower/upper.

On side note that code has a slight issue using the range version of std::ctype::toupper/lower, as std::basic_string isn't guaranteed to hold its elements contiguously in memory (although alot of imps still do) so the safest thing to do is to use something like std::transform with std::ctype::touppwer/lower single character version.

Thanks for the info. I actually remember reading something by P.J. Plauger (I think, it was C/C++ User's Journal) that said no current implementations take advantage of the leeway the Standard provides w.r.t. non-contiguous storage. But better to be safe, indeed.
--Michael Fawcett

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.