Ich möchte a konvertieren std::string
in Kleinbuchstaben. Die Funktion ist mir bekannt tolower()
. In der Vergangenheit hatte ich jedoch Probleme mit dieser Funktion und es ist sowieso kaum ideal, da sie mit a verwendet wird std::string
würde es erfordern, über jedes Zeichen zu iterieren.
Gibt es eine Alternative, die zu 100% funktioniert?
Angepasst von Nicht so häufig gestellte Fragen:
#include <algorithm>
#include <cctype>
#include <string>
std::string data = "Abc";
std::transform(data.begin(), data.end(), data.begin(),
[](unsigned char c){ return std::tolower(c); });
Sie werden wirklich nicht davonkommen, ohne durch jeden Charakter zu iterieren. Es gibt sonst keine Möglichkeit zu wissen, ob das Zeichen klein- oder großgeschrieben ist.
Wenn Sie wirklich hassen tolower()
hier ist eine spezialisierte Nur-ASCII-Alternative, die ich Ihnen nicht empfehle:
char asciitolower(char in) {
if (in <= 'Z' && in >= 'A')
return in - ('Z' - 'z');
return in;
}
std::transform(data.begin(), data.end(), data.begin(), asciitolower);
Beachten Sie, dass tolower()
kann nur eine Ersetzung pro einzelnem Byte-Zeichen vornehmen, was für viele Skripte unpassend ist, insbesondere wenn eine Multi-Byte-Codierung wie UTF-8 verwendet wird.
Boost stellt dafür einen String-Algorithmus zur Verfügung:
#include <boost/algorithm/string.hpp>
std::string str = "HELLO, WORLD!";
boost::algorithm::to_lower(str); // modifies str
Oder für nicht vorhanden:
#include <boost/algorithm/string.hpp>
const std::string str = "HELLO, WORLD!";
const std::string lower_str = boost::algorithm::to_lower_copy(str);
tl;dr
Verwenden Sie die Bibliothek der Intensivstation. Wenn Sie dies nicht tun, wird Ihre Konvertierungsroutine bei Fällen stillschweigend unterbrochen, von denen Sie wahrscheinlich nicht einmal wissen, dass sie existieren.
Zuerst müssen Sie eine Frage beantworten: Was ist das Codierung von dir std::string
? Ist es ISO-8859-1? Oder vielleicht ISO-8859-8? Oder Windows-Codepage 1252? Weiß das, was auch immer Sie verwenden, um Groß- in Kleinbuchstaben umzuwandeln? (Oder scheitert es kläglich für Charaktere über 0x7f
?)
Wenn Sie UTF-8 verwenden (die einzig vernünftige Wahl unter den 8-Bit-Codierungen) mit std::string
als Container täuscht man sich schon selbst, wenn man glaubt, die Dinge noch unter Kontrolle zu haben. Sie speichern eine Multibyte-Zeichenfolge in einem Container, der das Multibyte-Konzept nicht kennt, und die meisten Operationen, die Sie darauf ausführen können, auch nicht! Sogar etwas so Einfaches wie .substr()
könnte zu ungültigen (Teil-)Strings führen, weil Sie mitten in einer Multibyte-Sequenz teilen.
Sobald Sie so etwas versuchen std::toupper( 'ß' )
oder std::tolower( 'Σ' )
in irgendein Codierung, Sie sind in Schwierigkeiten. Weil 1) der Standard immer nur ein Zeichen gleichzeitig bearbeitet, kann er sich also einfach nicht drehen ß
hinein SS
wie es richtig wäre. Und 2) der Standard arbeitet immer nur mit einem Zeichen auf einmal, also kann er nicht entscheiden, ob Σ
mitten in einem Wort steht (wo σ
richtig wäre) oder am Ende (ς
). Ein anderes Beispiel wäre std::tolower( 'I' )
was zu anderen Ergebnissen führen sollte je nach Gebietsschema – praktisch überall, wo Sie es erwarten würden i
sondern in der Türkei ı
(LATEINISCHER KLEINBUCHSTABE DOTLOS I) ist die richtige Antwort (was wiederum mehr als ein Byte in der UTF-8-Codierung ist).
Damit, irgendein Groß-/Kleinschreibung, die jeweils für einen Charakter funktioniert, oder schlimmer noch, a Byte zu einer Zeit, wird durch Design gebrochen. Dazu gehören alle std::
zu diesem Zeitpunkt existierende Varianten.
Dann gibt es den Punkt, dass die Standardbibliothek, wofür sie ist ist in der Lage ist, hängt davon ab, welche Gebietsschemas sind unterstützt auf dem Computer, auf dem Ihre Software läuft … und was tun Sie, wenn Ihr Zielgebietsschema auf dem Computer Ihres Clients nicht unterstützt wird?
Also was du bist Ja wirklich Suche nach einer String-Klasse, die in der Lage ist, mit all dem richtig umzugehen, und das ist nicht irgendeiner der std::basic_string<>
Varianten.
(C++11-Hinweis: std::u16string
und std::u32string
sind besser, aber immer noch nicht perfekt. C++20 gebracht std::u8string
aber alles, was diese tun, ist die Angabe Codierung. In vielerlei anderer Hinsicht wissen sie immer noch nichts über die Unicode-Mechanik, wie Normalisierung, Sortierung, …)
Während Boost sieht aus Schön, API-weise, Boost.Locale ist im Grunde ein Wrapper Intensivstation. Wenn Boost ist zusammengestellt mit ICU-Unterstützung … wenn nicht, ist Boost.Locale auf die für die Standardbibliothek kompilierte Gebietsschemaunterstützung beschränkt.
Und glaub mir, bekommen Boost zum Kompilieren mit ICU kann manchmal ein echter Schmerz sein. (Es gibt keine vorkompilierten Binärdateien für Windows, die ICU enthalten, also müssten Sie sie zusammen mit Ihrer Anwendung bereitstellen, und das öffnet eine ganz neue Dose Würmer…)
Ich persönlich würde also empfehlen, die volle Unicode-Unterstützung direkt aus dem Maul des Pferdes zu bekommen und die Intensivstation Bibliothek direkt:
#include <unicode/unistr.h>
#include <unicode/ustream.h>
#include <unicode/locid.h>
#include <iostream>
int main()
{
/* "Odysseus" */
char const * someString = u8"ΟΔΥΣΣΕΥΣ";
icu::UnicodeString someUString( someString, "UTF-8" );
// Setting the locale explicitly here for completeness.
// Usually you would use the user-specified system locale,
// which *does* make a difference (see ı vs. i above).
std::cout << someUString.toLower( "el_GR" ) << "\n";
std::cout << someUString.toUpper( "el_GR" ) << "\n";
return 0;
}
Kompilieren (in diesem Beispiel mit G++):
g++ -Wall example.cpp -licuuc -licuio
Das gibt:
ὀδυσσεύς
Beachten Sie, dass die Σ<->σ-Umwandlung in der Mitte des Wortes und die Σ<->ς-Umwandlung am Ende des Wortes steht. Nein <algorithm>
-basierte Lösung kann Ihnen das geben.
Mit der bereichsbasierten for-Schleife von C++11 wäre ein einfacherer Code:
#include <iostream> // std::cout
#include <string> // std::string
#include <locale> // 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);
}
Ein weiterer Ansatz, der eine bereichsbasierte For-Schleife mit Referenzvariable verwendet
string test = "Hello World";
for(auto& c : test)
{
c = tolower(c);
}
cout<<test<<endl;
Dies ist eine Fortsetzung der Antwort von Stefan Mai: Wenn Sie das Ergebnis der Konvertierung in einem anderen String platzieren möchten, müssen Sie dessen Speicherplatz vor dem Aufrufen vorbelegen std::transform
. Da STL transformierte Zeichen im Ziel-Iterator speichert (inkrementiert bei jeder Iteration der Schleife), wird die Größe des Ziel-Strings nicht automatisch angepasst, und Sie riskieren Speicherverlust.
#include <string>
#include <algorithm>
#include <iostream>
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;
}
Wie sonst würden Sie jedes Element einer Liste von irgendetwas in etwas anderes umwandeln, ohne die Liste zu durchlaufen? Eine Zeichenfolge ist nur eine Liste von Zeichen. Wenn Sie auf jedes Zeichen eine Funktion anwenden müssen, müssen Sie die Zeichenfolge durchlaufen. Daran führt kein Weg vorbei.
– Benutzer21037
24. November 2008 um 12:14 Uhr
Warum genau wird diese Frage heruntergestuft? Ich habe kein Problem damit, meinen String zu durchlaufen, aber ich frage, ob es außer tolower(), toupper() usw. noch andere Funktionen gibt.
– Konrad
24. November 2008 um 12:24 Uhr
Wenn Sie ein Zeichenarray im C-Stil haben, können Sie möglicherweise ox20202020 zu jedem Block mit 4 Zeichen hinzufügen (vorausgesetzt, sie sind ALLE bereits in Großbuchstaben), um jeweils 4 Zeichen in Kleinbuchstaben umzuwandeln.
– Benutzer21037
24. November 2008 um 13:05 Uhr
@Dan: Wenn sie möglicherweise bereits Kleinbuchstaben sind, aber definitiv AZ oder az sind, können Sie mit 0x20 ODER mit 0x20 statt hinzufügen. Eine dieser so schlauen, wahrscheinlich dummen Optimierungen, die sich fast nie lohnen …
– Steve Jessop
24. November 2008 um 13:11 Uhr
Ich weiß nicht, warum es abgelehnt worden wäre … sicherlich ist es etwas seltsam formuliert (weil Sie irgendwie jeden Punkt durchlaufen müssen), aber es ist eine berechtigte Frage
– Warren
24. November 2008 um 13:19 Uhr