Wie konvertiert man std :: string in Kleinbuchstaben?

Ich möchte eine std::string in Kleinbuchstaben konvertieren. Ich bin mir der function tolower() bewusst, aber in der Vergangenheit hatte ich Probleme mit dieser function und sie ist sowieso kaum ideal, da die Verwendung mit einem std::string ein Iterieren über jedes Zeichen erfordern würde.

Gibt es eine Alternative, die zu 100% funktioniert?

Von diesem :

 #include  #include  std::string data = "Abc"; std::transform(data.begin(), data.end(), data.begin(), ::tolower); 

Du wirst wirklich nicht damit durchkommen, jeden Charakter zu durchlaufen. Es gibt keine Möglichkeit zu wissen, ob das Zeichen in Klein- oder Großbuchstaben geschrieben wurde.

Wenn Sie tolower() wirklich hassen, hier ist eine nicht portable Alternative, die ich nicht empfehlen kann:

 char easytolower(char in) { if(in < = 'Z' && in >= 'A') return in - ('Z' - 'z'); return in; } std::transform(data.begin(), data.end(), data.begin(), easytolower); 

::tolower() dass ::tolower() nur eine Substitution pro einzelnen Byte-Zeichen durchführen kann, was für viele Skripte nicht ::tolower() ist, insbesondere wenn eine Multi-Byte-Codierung wie UTF-8 verwendet wird.

Dafür gibt es einen Boost-String-Algorithmus:

 #include  std::string str = "HELLO, WORLD!"; boost::algorithm::to_lower(str); // modifies str 

Oder, für nicht-in-place:

 #include  const std::string str = "HELLO, WORLD!"; const std::string lower_str = boost::algorithm::to_lower_copy(str); 

tl; dr

Verwenden Sie die ICU-Bibliothek .


Zuerst müssen Sie eine Frage beantworten: Wie lautet die Kodierung Ihrer std::string ? Ist es ISO-8859-1? Oder vielleicht ISO-8859-8? Oder Windows Codepage 1252? Was weiß man, wenn man Groß- und Kleinbuchstaben konvertiert? (Oder scheitert es kläglich für Zeichen über 0x7f ?)

Wenn Sie UTF-8 (die einzige vernünftige Wahl unter den 8-Bit-Kodierungen) mit std::string als Container verwenden, täuschen Sie sich bereits selbst zu glauben, dass Sie immer noch die Kontrolle über Dinge haben, weil Sie ein Multibyte-Zeichen speichern Sequenz in einem Container, der das Multibyte-Konzept nicht kennt. Selbst etwas so einfaches wie .substr() ist eine tickende Zeitbombe. (Da das Aufteilen einer Multibyte-Sequenz zu einer ungültigen (Teil-) Zeichenfolge führt.)

Und sobald du etwas wie std::toupper( 'ß' ) , bist du in jeder Kodierung in großen Schwierigkeiten. (Weil es einfach nicht möglich ist, dies mit der Standardbibliothek “richtig” zu machen, die nur ein Ergebniszeichen liefern kann, nicht das hier benötigte "SS" .) [1] Ein anderes Beispiel wäre std::tolower( 'I' ) , die je nach Gebietsschema unterschiedliche Ergebnisse liefern. In Deutschland wäre 'i' korrekt; in der Türkei ist 'ı' (LATIN SMALL LETTER DOTLESS I) das erwartete Ergebnis.

Dann gibt es den Punkt, dass die Standardbibliothek davon abhängt, welche Gebietsschemas auf dem Computer, auf dem Ihre Software läuft, unterstützt werden … und was tun Sie, wenn dies nicht der Fall ist?

Also, was Sie wirklich suchen, ist eine String-class, die in der Lage ist, mit all dem richtig umzugehen, und das ist nicht std::string .

(C ++ 11 Anmerkung: std::u16string und std::u32string sind besser , aber immer noch nicht perfekt.)

Während Boost gut aussieht , API, ist Boost.Locale im Grunde genommen ein Wrapper auf der Intensivstation . Wenn Boost mit ICU-Unterstützung kompiliert wird, ist Boost.Locale auf die Locale-Unterstützung beschränkt, die für die Standardbibliothek kompiliert wurde.

Und glauben Sie mir, Boost zu kompilieren mit ICU kann manchmal ein echter Schmerz sein. (Es gibt keine vorkompilierten Binärdateien für Windows, daher müssten Sie sie zusammen mit Ihrer Anwendung bereitstellen, und das öffnet eine ganz neue Dose Würmer …)

So persönlich würde ich empfehlen, die volle Unicode-Unterstützung direkt aus dem Mund des Pferdes zu bekommen und die ICU- Bibliothek direkt zu verwenden:

 #include  #include  #include  #include  int main() { char const * someString = "Eidenges\xe4\xdf"; icu::UnicodeString someUString( someString, "ISO-8859-1" ); // Setting the locale explicitly here for completeness. // Usually you would use the user-specified system locale. std::cout < < someUString.toLower( "de_DE" ) << "\n"; std::cout << someUString.toUpper( "de_DE" ) << "\n"; return 0; } 

Kompilieren (mit G ++ in diesem Beispiel):

 g++ -Wall example.cpp -licuuc -licuio 

Das gibt:

 eidengesäß EIDENGESÄSS 

[1] Im Jahr 2017 entschied der Rat für deutsche Rechtschreibung, dass "+" U + 1E9E LATIN CAPITAL LETTER SHARP S offiziell neben der traditionellen "SS" -Umwandlung verwendet werden könnte, um Mehrdeutigkeiten zu vermeiden, zB in Pässen (wo Namen groß geschrieben werden) ). Mein schönes Beispiel, durch Ausschussentscheidung obsolet geworden ...

Wenn die Zeichenfolge UTF-8-Zeichen außerhalb des ASCII-Bereichs enthält, konvertiert boost :: algorithm :: to_lower diese nicht. Verwenden Sie besser boost :: locale :: to_lower, wenn UTF-8 beteiligt ist. Siehe http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/conversions.html

Unter Verwendung der bereichsbasierten for-Schleife von C ++ 11 wäre ein einfacherer Code:

 #include  // std::cout #include  // std::string #include  // std::locale, std::tolower int main () { std::locale loc; std::string str="Test String.\n"; for(auto elem : str) std::cout < < std::tolower(elem,loc); } 

Dies ist eine Fortsetzung der Antwort von Stefan Mai: Wenn Sie das Ergebnis der Konvertierung in eine andere Zeichenfolge std::transform , müssen Sie seinen Speicherplatz vor dem Aufruf von std::transform vorbelegen. Da STL transformierte Zeichen im Ziel-Iterator speichert (inkrementiert sie bei jeder Iteration der Schleife), wird die Zielzeichenfolge nicht automatisch in der Größe geändert, und Sie riskieren Speicherstampfen.

 #include  #include  #include  int main (int argc, char* argv[]) { std::string sourceString = "Abc"; std::string destinationString; // Allocate the destination space destinationString.resize(sourceString.size()); // Convert the source string to lower case // storing the result in destination string std::transform(sourceString.begin(), sourceString.end(), destinationString.begin(), ::tolower); // Output the result of the conversion std::cout < < sourceString << " -> " < < destinationString << std::endl; } 

Soweit ich sehe Boost-Bibliotheken sind wirklich leistungsmäßig schlecht. Ich habe ihre unordered_map zu STL getestet und es war durchschnittlich 3 mal langsamer (bester Fall 2, schlimmster war 10 mal). Auch dieser Algorithmus sieht zu niedrig aus.

Der Unterschied ist so groß, dass ich mir sicher bin, dass jeder Zusatz, den du tun tolower , um es zu erhöhen, um “für deine Bedürfnisse” zu erhöhen, viel schneller sein wird als Boost.

Ich habe diese Tests an einem Amazon EC2 gemacht, daher variierte die performance während des Tests, aber Sie haben immer noch die Idee.

 ./test Elapsed time: 12365milliseconds Elapsed time: 1640milliseconds ./test Elapsed time: 26978milliseconds Elapsed time: 1646milliseconds ./test Elapsed time: 6957milliseconds Elapsed time: 1634milliseconds ./test Elapsed time: 23177milliseconds Elapsed time: 2421milliseconds ./test Elapsed time: 17342milliseconds Elapsed time: 14132milliseconds ./test Elapsed time: 7355milliseconds Elapsed time: 1645milliseconds 

-O2 hat es so gemacht:

 ./test Elapsed time: 3769milliseconds Elapsed time: 565milliseconds ./test Elapsed time: 3815milliseconds Elapsed time: 565milliseconds ./test Elapsed time: 3643milliseconds Elapsed time: 566milliseconds ./test Elapsed time: 22018milliseconds Elapsed time: 566milliseconds ./test Elapsed time: 3845milliseconds Elapsed time: 569milliseconds 

Quelle:

 string str; bench.start(); for(long long i=0;i<1000000;i++) { str="DSFZKMdskfdsjfsdfJDASFNSDJFXCKVdnjsafnjsdfjdnjasnJDNASFDJDSFSDNJjdsanjfsdnfjJNFSDJFSD"; boost::algorithm::to_lower(str); } bench.end(); bench.start(); for(long long i=0;i<1000000;i++) { str="DSFZKMdskfdsjfsdfJDASFNSDJFXCKVdnjsafnjsdfjdnjasnJDNASFDJDSFSDNJjdsanjfsdnfjJNFSDJFSD"; for(unsigned short loop=0;loop < str.size();loop++) { str[loop]=tolower(str[loop]); } } bench.end(); 

Ich denke, ich sollte zu den Tests auf einer dedizierten Maschine, aber ich werde diese EC2 verwenden, also muss ich es nicht wirklich auf meinem Rechner testen.

std::ctype::tolower() aus der C ++ Localization-Standardbibliothek führt dies korrekt für Sie aus. Hier ist ein Beispiel aus der Referenzseite von tolower

 #include  #include  int main () { std::locale::global(std::locale("en_US.utf8")); std::wcout.imbue(std::locale()); std::wcout < < "In US English UTF-8 locale:\n"; auto& f = std::use_facet>(std::locale()); std::wstring str = L"HELLo, wORLD!"; std::wcout < < "Lowercase form of the string '" << str << "' is "; f.tolower(&str[0], &str[0] + str.size()); std::wcout << "'" << str << "'\n"; } 

Der einfachste Weg, um einen String in einen Low-Case umzuwandeln, ohne sich um den std-Namespace kümmern zu müssen, ist wie folgt

1: Zeichenfolge mit / ohne Leerzeichen

 #include  #include  #include  using namespace std; int main(){ string str; getline(cin,str); //------------function to convert string into lowercase--------------- transform(str.begin(), str.end(), str.begin(), ::tolower); //-------------------------------------------------------------------- cout<  

2: Zeichenfolge ohne Leerzeichen

 #include  #include  #include  using namespace std; int main(){ string str; cin>>str; //------------function to convert string into lowercase--------------- transform(str.begin(), str.end(), str.begin(), ::tolower); //-------------------------------------------------------------------- cout<  

Ein weiterer Ansatz, bei dem eine bereichsabhängige Schleife mit einer Referenzvariablen verwendet wird

 string test = "Hello World"; for(auto& c : test) { c = tolower(c); } cout<  

Eine Alternative zu Boost ist POCO (pocoproject.org).

POCO bietet zwei Varianten:

  1. Die erste Variante erstellt eine Kopie, ohne die ursprüngliche Zeichenfolge zu ändern.
  2. Die zweite Variante ändert die ursprüngliche Zeichenfolge an Ort und Stelle.
    “In Place” -Versionen haben immer “InPlace” im Namen.

Beide Versionen werden im Folgenden gezeigt:

 #include "Poco/String.h" using namespace Poco; std::string hello("Stack Overflow!"); // Copies "STACK OVERFLOW!" into 'newString' without altering 'hello.' std::string newString(toUpper(hello)); // Changes newString in-place to read "stack overflow!" toLowerInPlace(newString); 

Es gibt eine Möglichkeit, Großbuchstaben in weniger zu konvertieren , wenn Tests durchgeführt werden , und es ist ziemlich einfach. Die Verwendung der function isupper () / Makros von clocale.h sollte sich um Probleme kümmern, die sich auf Ihren Standort beziehen, aber wenn nicht, können Sie die UtoL [] nach Herzenslust optimieren.

Wenn man bedenkt, dass die Zeichen von C wirklich nur aus 8 Bit bestehen (ignorieren Sie die großen Zeichensätze für den Moment), können Sie ein Array von 256 Byte erstellen, das einen alternativen Satz von Zeichen enthält, und in der Konvertierungsfunktion die Zeichen in Ihrem String als Indizes verwenden Umwandlungsarray.

Anstelle einer 1-für-1-Zuordnung geben Sie den Array-Mitgliedern in Großbuchstaben die BYTE-int-Werte für die Kleinbuchstaben. Sie können islower () und isupper () hier nützlich finden.

Bildbeschreibung hier eingeben

Der Code sieht so aus …

 #include  static char UtoL[256]; // ---------------------------------------------------------------------------- void InitUtoLMap() { for (int i = 0; i < sizeof(UtoL); i++) { if (isupper(i)) { UtoL[i] = (char)(i + 32); } else { UtoL[i] = i; } } } // ---------------------------------------------------------------------------- char *LowerStr(char *szMyStr) { char *p = szMyStr; // do conversion in-place so as not to require a destination buffer while (*p) { // szMyStr must be null-terminated *p = UtoL[*p]; p++; } return szMyStr; } // ---------------------------------------------------------------------------- int main() { time_t start; char *Lowered, Upper[128]; InitUtoLMap(); strcpy(Upper, "Every GOOD boy does FINE!"); Lowered = LowerStr(Upper); return 0; } 

Auf diese Weise können Sie auch alle anderen Zeichen neu zuordnen, die Sie ändern möchten.

Dieser Ansatz hat einen großen Vorteil, wenn er auf modernen processoren ausgeführt wird, da keine Branch-Prädiktion erforderlich ist, da keine Branching-Tests vorhanden sind. Dies speichert die Verzweigungsvorhersagelogik der CPU für andere Schleifen und neigt dazu, Pipeline-Blockierungen zu verhindern.

Einige mögen diesen Ansatz als den gleichen erkennen, der zum Konvertieren von EBCDIC in ASCII verwendet wurde.

Hier ist eine Makro-Technik, wenn Sie etwas Einfaches wollen:

 #define STRTOLOWER(x) std::transform (x.begin(), x.end(), x.begin(), ::tolower) #define STRTOUPPER(x) std::transform (x.begin(), x.end(), x.begin(), ::toupper) #define STRTOUCFIRST(x) std::transform (x.begin(), x.begin()+1, x.begin(), ::toupper); std::transform (x.begin()+1, x.end(), x.begin()+1,::tolower) 

Beachten Sie jedoch, dass @ AndreasSpindlers Kommentar zu dieser Antwort immer noch eine wichtige Überlegung ist, wenn Sie an etwas arbeiten, das nicht nur aus ASCII-Zeichen besteht.

 // tolower example (C++) #include  // std::cout #include  // std::string #include  // std::locale, std::tolower int main () { std::locale loc; std::string str="Test String.\n"; for (std::string::size_type i=0; i 

Für weitere Informationen: http://www.cplusplus.com/reference/locale/tolower/

Auf Microsoft-Plattformen können Sie die functionenfamilie strlwr verwenden: http://msdn.microsoft.com/en-us/library/hkxwh33z.aspx

 // crt_strlwr.c // compile with: /W3 // This program uses _strlwr and _strupr to create // uppercase and lowercase copies of a mixed-case string. #include  #include  int main( void ) { char string[100] = "The String to End All Strings!"; char * copy1 = _strdup( string ); // make two copies char * copy2 = _strdup( string ); _strlwr( copy1 ); // C4996 _strupr( copy2 ); // C4996 printf( "Mixed: %s\n", string ); printf( "Lower: %s\n", copy1 ); printf( "Upper: %s\n", copy2 ); free( copy1 ); free( copy2 ); } 

Code-Auszug

 #include using namespace std; int main () { ios::sync_with_stdio(false); string str="String Convert\n"; for(int i=0; i 

Verwenden Sie fplus :: to_lower_case ().

(fplus: https://github.com/Dobiasd/functionalPlus .

Suche ‘to_lower_case’ in http://www.editgym.com/fplus-api-search/ )

 fplus::to_lower_case(std::string("ABC")) == std::string("abc"); 

Kopieren, weil es nicht erlaubt war, die Antwort zu verbessern. Danke SO


 string test = "Hello World"; for(auto& c : test) { c = tolower(c); } 

Erläuterung:

for(auto& c : test) ist eine bereichsbasierte for-Schleife der Art
for ( range_declaration : range_expression ) loop_statement :

  1. range_declaration : auto& c
    Hier wird der automatische Spezifizierer für den automatischen Typabzug verwendet. Der Typ wird also vom Initialisierer der Variablen abgezogen.

  2. range_expression : test
    Der Bereich in diesem Fall sind die Zeichen des Stringtests.

Die Zeichen des Stringtests stehen innerhalb der for-Schleife through-Kennung c als Referenz zur Verfügung.

Dies könnte eine andere einfache Version sein, um Großbuchstaben in Kleinbuchstaben und umgekehrt umzuwandeln. Ich habe VS2017-Community-Version verwendet, um diesen Quellcode zu kompilieren.

 #include  #include  using namespace std; int main() { std::string _input = "lowercasetouppercase"; #if 0 // My idea is to use the ascii value to convert char upperA = 'A'; char lowerA = 'a'; cout < < (int)upperA << endl; // ASCII value of 'A' -> 65 cout < < (int)lowerA << endl; // ASCII value of 'a' -> 97 // 97-65 = 32; // Difference of ASCII value of upper and lower a #endif // 0 cout < < "Input String = " << _input.c_str() << endl; for (int i = 0; i < _input.length(); ++i) { _input[i] -= 32; // To convert lower to upper #if 0 _input[i] += 32; // To convert upper to lower #endif // 0 } cout << "Output String = " << _input.c_str() << endl; return 0; } 

Hinweis: Wenn Sonderzeichen vorhanden sind, müssen diese mit der Bedingungsprüfung behandelt werden.

Ich habe versucht std :: transform, alles, was ich bekomme ist abscheulich stl criptic Kompilierung Fehler, die nur Druiden von vor 200 Jahren verstehen können (kann nicht von flibidi flabidi Grippe konvertieren)

das funktioniert gut und kann leicht angepasst werden

 string LowerCase(string s) { int dif='a'-'A'; for(int i=0;i='A')&&(s[i]< ='Z')) s[i]+=dif; } return s; } string UpperCase(string s) { int dif='a'-'A'; for(int i=0;i='a')&&(s[i]< ='z')) s[i]-=dif; } return s; } 
 //You can really just write one on the fly whenever you need one. #include  void _lower_case(std::string& s){ for(unsigned short l = s.size();l;s[--l]|=(1< <5)); } //Here is an example. //http://ideone.com/mw2eDK