Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

std::string, std::transform und tolower

29 views
Skip to first unread message

Robert Hartmann

unread,
Dec 9, 2011, 2:58:01 PM12/9/11
to
Hallo zusammen,


Es ist ja keine Schwierigkeit fᅵr ASCII-Zeichen
eine eigene Funktion zu schreiben, die Groᅵ
in Kleinbuchstaben umwandelt.

Ich wollte mal "faul" sein und Vorhandenes nutzen:

Aber bei dem Zusammenspiel von std::string, std::transform
mit std::tolower spuckt mir irgendeine indirekte Headerdatei
eine ᅵberladung von tolower hinein. In meinem eigenen Code
benutze ich kein "using namespace std;"


Hier mein Ansatz und g++ (GCC) 4.5.2 Fehlermeldung:

inline void tolowerCase(std::string& str){
std::transform(str.begin(), str.end(), str.begin(), std::tolower);
}

error: no matching function for call to
'transform(std::basic_string<char>::iterator,
std::basic_string<char>::iterator, std::basic_string<char>::iterator,
<unresolved overloaded function type>)'


Zwei Ansᅵtze nach "herum-googeln" geben keine Compilerfehlermeldungen:

1.)
std::transform(str.begin(), str.end(), str.begin(), ::tolower);

2.)
std::transform(str.begin(), str.end(), str.begin(),
(int (*)(int))tolower);



Fall 1 holt sich also eine tolower, die nicht im namespace std liegt.
Woher die auch immer kommt?
Fall 2 wird, zumindest sieht das fᅵr mich so aus, ein Typecast pointer
nach int gemacht, aber warum das hilft ... ist mir nicht ganz klar.

Auf lehrreiche Antworten hoffend,

Gruᅵ Robert

.

Stefan Reuther

unread,
Dec 10, 2011, 1:35:15 PM12/10/11
to
Robert Hartmann wrote:
> Hier mein Ansatz und g++ (GCC) 4.5.2 Fehlermeldung:
>
> inline void tolowerCase(std::string& str){
> std::transform(str.begin(), str.end(), str.begin(), std::tolower);
> }

Selbst, wenn das compilieren würde, hätte das das Problem, dass du den
Kontrakt von 'tolower' verletzt. 'tolower' will ein nach 'unsigned char'
gecastetes Zeichen haben, 'std::string::value_type' ist aber 'char'.

Wenn schon, dann irgendwas der Art
char toLowerChar(char c) {
return std::tolower(static_cast<unsigned char>(c));
}
// ...
std::transform(str.begin(), str.end(), str.begin(), toLowerChar);
(Disclaimer: ungetestet)

> error: no matching function for call to
> 'transform(std::basic_string<char>::iterator,
> std::basic_string<char>::iterator, std::basic_string<char>::iterator,
> <unresolved overloaded function type>)'

Möglicherweise stolpert er über die Überladung
template <class charT> charT tolower(charT c, const locale& loc);
aus <locale>, welcher über zahlreiche andere Header (z.B. die Streams)
mitkommt.

> Zwei Ansätze nach "herum-googeln" geben keine Compilerfehlermeldungen:
>
> 1.)
> std::transform(str.begin(), str.end(), str.begin(), ::tolower);
>
> 2.)
> std::transform(str.begin(), str.end(), str.begin(),
> (int (*)(int))tolower);
>
> Fall 1 holt sich also eine tolower, die nicht im namespace std liegt.
> Woher die auch immer kommt?

Aus <ctype.h>.

> Fall 2 wird, zumindest sieht das für mich so aus, ein Typecast pointer
> nach int gemacht, aber warum das hilft ... ist mir nicht ganz klar.

Das löst eine überladene Funktion auf. Nur die von dir gewünschte
Überladung lässt sich nach (int (*)(int)) casten.

Allerdings ist meine Erfahrung, dass die Verwendung von <cctype>- und
<cstring>-Funktionen in C++ in der Praxis durchaus steinig ist. Gerade
deinen Fall hatte ich neulich aus dem Code eines Kollegen rausoperiert,
weil es einer der verwendeten Compiler ums Verrecken nicht fressen
wollte. Bei älteren Compilern/Bibliotheken schießen dann bei sowas auch
gerne mal Makros quer (Borland macht '#define strcpy __strcpy__',
Ergebnis: ''__strcpy__' is not a member of 'std'').


Stefan

Thomas Maeder

unread,
Dec 11, 2011, 1:28:38 PM12/11/11
to
Stefan Reuther <stefa...@arcor.de> writes:

> Robert Hartmann wrote:
>> Hier mein Ansatz und g++ (GCC) 4.5.2 Fehlermeldung:
>>
>> inline void tolowerCase(std::string& str){
>> std::transform(str.begin(), str.end(), str.begin(), std::tolower);
>> }
>
> Selbst, wenn das compilieren würde, hätte das das Problem, dass du den
> Kontrakt von 'tolower' verletzt. 'tolower' will ein nach 'unsigned char'
> gecastetes Zeichen haben, 'std::string::value_type' ist aber 'char'.
>
> Wenn schon, dann irgendwas der Art
> char toLowerChar(char c) {

An diese Stelle würde ich unbedingt

assert(c>=0);
assert(c<=127);

einfügen, damit sichergestellt ist, dass es sich bei c tatsächlich wie
verlangt um ein ASCII-Zeichen handelt.

Und auch dann wird die korrekte Umwandlung auf Eurer Seite des Rheins
wohl am "scharfen s" scheitern, welches in Grossbuchstaben zumeist als
"SS" geschrieben wird.

> return std::tolower(static_cast<unsigned char>(c));
> }

--- Posted via news://freenews.netfront.net/ - Complaints to ne...@netfront.net ---

Stefan Reuther

unread,
Dec 13, 2011, 12:17:37 PM12/13/11
to
Thomas Maeder wrote:
> Stefan Reuther <stefa...@arcor.de> writes:
>>Robert Hartmann wrote:
>>>Hier mein Ansatz und g++ (GCC) 4.5.2 Fehlermeldung:
>>>inline void tolowerCase(std::string& str){
>>>std::transform(str.begin(), str.end(), str.begin(), std::tolower);
>>>}
>>
>>Selbst, wenn das compilieren würde, hätte das das Problem, dass du den
>>Kontrakt von 'tolower' verletzt. 'tolower' will ein nach 'unsigned char'
>>gecastetes Zeichen haben, 'std::string::value_type' ist aber 'char'.
>>
>>Wenn schon, dann irgendwas der Art
>> char toLowerChar(char c) {
>
> An diese Stelle würde ich unbedingt
>
> assert(c>=0);
> assert(c<=127);
>
> einfügen, damit sichergestellt ist, dass es sich bei c tatsächlich wie
> verlangt um ein ASCII-Zeichen handelt.

Nein, wozu? std::tolower kann mit Zeichen umgehen, die nicht aus ASCII
kommen. Vorausgesetzt, man castet den Parameter vorher korrekt nach
'unsigned char'.

(Und davon abgesehen, dass der zugrundeliegende Zeichensatz nicht ASCII
sein muss. Ein Motorola 56000 könnte, wenn er wöllte, mit einem
'unsigned char' den kompletten Unicode darstellen.)

> Und auch dann wird die korrekte Umwandlung auf Eurer Seite des Rheins
> wohl am "scharfen s" scheitern, welches in Grossbuchstaben zumeist als
> "SS" geschrieben wird.

Das wird er dann halt nicht großmachen, aber daran ist man diesseits des
Rheins auch gewohnt.


Stefan

Robert Hartmann

unread,
Dec 16, 2011, 4:36:54 AM12/16/11
to
Am 13.12.2011 18:17, schrieb Stefan Reuther:
> Thomas Maeder wrote:
>> Stefan Reuther<stefa...@arcor.de> writes:
>>> Robert Hartmann wrote:
>>>> Hier mein Ansatz und g++ (GCC) 4.5.2 Fehlermeldung:
>>>> inline void tolowerCase(std::string& str){
>>>> std::transform(str.begin(), str.end(), str.begin(), std::tolower);
>>>> }
>>>
>>> Selbst, wenn das compilieren würde, hätte das das Problem, dass du den
>>> Kontrakt von 'tolower' verletzt. 'tolower' will ein nach 'unsigned char'
>>> gecastetes Zeichen haben, 'std::string::value_type' ist aber 'char'.
>>>
>>> Wenn schon, dann irgendwas der Art
>>> char toLowerChar(char c) {
>>
>> An diese Stelle würde ich unbedingt
>>
>> assert(c>=0);
>> assert(c<=127);
>>
>> einfügen, damit sichergestellt ist, dass es sich bei c tatsächlich wie
>> verlangt um ein ASCII-Zeichen handelt.
>
> Nein, wozu? std::tolower kann mit Zeichen umgehen, die nicht aus ASCII
> kommen. Vorausgesetzt, man castet den Parameter vorher korrekt nach
> 'unsigned char'.

Stefan, die Idee von Thomas mit den asserts keine schlechte Idee,
da er tatsächlich davon ausgehen musste, dass mich nur die Umwandlung
von ASCII-Zeichen interessiere. :-)

> (Und davon abgesehen, dass der zugrundeliegende Zeichensatz nicht ASCII
> sein muss. Ein Motorola 56000 könnte, wenn er wöllte, mit einem
> 'unsigned char' den kompletten Unicode darstellen.)

Wo du gerade von Unicode sprichst, wie würde man einen std::wstring
in Kleinbuchstaben transformieren?
Meine Vermutung ist, dass ich dazu den wstring pro Zeichen wchar_t
umzuwandeln habe, evtl unter Nutzung von locale?


wchar_t toLowerWChar(wchar_t c) {

/* Ich hab noch keine Idee,
aber auch noch nicht wirklich überlegt,
oder gesucht.
Bitte noch kein Lösungsquelltext geben.
Gerne aber ein Hinweis auf ein Tutorial :-)
*/

}


void tolowerCase(std::wstring& wstr){
std::transform(wstr.begin(), wstr.end(), wstr.begin(), toLowerWChar);
}


>
>> Und auch dann wird die korrekte Umwandlung auf Eurer Seite des Rheins
>> wohl am "scharfen s" scheitern, welches in Grossbuchstaben zumeist als
>> "SS" geschrieben wird.
>
> Das wird er dann halt nicht großmachen, aber daran ist man diesseits des
> Rheins auch gewohnt.

:-)

Gruß Robert

Stefan Reuther

unread,
Dec 16, 2011, 7:23:59 AM12/16/11
to
Robert Hartmann wrote:
> Am 13.12.2011 18:17, schrieb Stefan Reuther:
>> (Und davon abgesehen, dass der zugrundeliegende Zeichensatz nicht ASCII
>> sein muss. Ein Motorola 56000 könnte, wenn er wöllte, mit einem
>> 'unsigned char' den kompletten Unicode darstellen.)
>
> Wo du gerade von Unicode sprichst, wie würde man einen std::wstring
> in Kleinbuchstaben transformieren?
> Meine Vermutung ist, dass ich dazu den wstring pro Zeichen wchar_t
> umzuwandeln habe, evtl unter Nutzung von locale?
>
> wchar_t toLowerWChar(wchar_t c) {
>
> /* Ich hab noch keine Idee,
> aber auch noch nicht wirklich überlegt,
> oder gesucht.
> Bitte noch kein Lösungsquelltext geben.
> Gerne aber ein Hinweis auf ein Tutorial :-)
> */

Es gibt in <locale> die Funktion
template <class charT>
charT std::tolower(charT c, const std::locale& loc)
die für 'char' und 'wchar_t' funktionieren soll, und in <cwtype> bzw.
<wtype.h> die Funktion
wint_t towlower(wint_t)
aus der C-Bibliothek, die der 'tolower'-Funktion für 'char' entspricht.

Welchen Zeichenumfang die bedienen ist aber wie immer implementation-
defined, und ich würde nicht davon ausgehen, dass die in einer handels-
üblichen C++-Bibliothek wissen, dass der Kleinbuchstabe zu U+1040B
"DESERET CAPITAL LETTER SHORT OO" U+10433 "DESERET SMALL LETTER SHORT
OO" ist.


Stefan
0 new messages