1006 Stimmen

Wie konvertiert man eine Instanz von std::string in Kleinbuchstaben?

Ich möchte eine std::string zu Kleinbuchstaben. Die Funktion ist mir bekannt tolower() . In der Vergangenheit hatte ich jedoch Probleme mit dieser Funktion, und sie ist ohnehin nicht ideal, da sie mit einer std::string würde eine Iteration über jedes Zeichen erfordern.

Gibt es eine Alternative, die zu 100 % funktioniert?

1121voto

Stefan Mai Punkte 22433

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); });

Du wirst wirklich nicht weiterkommen, ohne jedes Zeichen zu wiederholen. Es gibt keine Möglichkeit zu wissen, ob das Zeichen Klein- oder Großbuchstaben sonst ist.

Wenn Sie wirklich hassen tolower() Hier ist eine spezielle ASCII-Alternative, deren Verwendung ich 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 Einzelbyte-Zeichen vornehmen, was für viele Skripte unpassend ist, insbesondere wenn sie eine Multi-Byte-Kodierung wie UTF-8 verwenden.

373voto

Rob Punkte 72944

Boost bietet einen String-Algorithmus für diese :

#include <boost/algorithm/string.hpp>

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

Oder, für nicht an Ort und Stelle :

#include <boost/algorithm/string.hpp>

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

349voto

DevSolar Punkte 63096

tl;dr

Verwenden Sie die Bibliothek der Intensivstation . Wenn Sie das nicht tun, wird Ihre Konvertierungsroutine in Fällen, von denen Sie wahrscheinlich nicht einmal wissen, dass sie existieren, unbemerkt abbrechen.


Zunächst müssen Sie eine Frage beantworten: Was ist die Kodierung Ihrer std::string ? Ist es ISO-8859-1? Oder vielleicht ISO-8859-8? Oder Windows Codepage 1252? Weiß das Programm, mit dem Sie Groß- in Kleinbuchstaben umwandeln wollen, das? (Oder scheitert sie kläglich bei Personen über 0x7f ?)

Wenn Sie UTF-8 verwenden ( die einzige vernünftige Wahl unter den 8-Bit-Kodierungen ) mit std::string als Container, täuschen Sie sich bereits, wenn Sie glauben, dass Sie die Dinge noch unter Kontrolle haben. Sie speichern eine Multibyte-Zeichenfolge in einem Container, der das Multibyte-Konzept nicht kennt, und auch die meisten der Operationen, die Sie damit durchführen können, nicht! Selbst etwas so Einfaches wie .substr() könnte zu ungültigen (Unter-)Zeichenketten führen, weil Sie in der Mitte einer Multibyte-Sequenz trennen.

Sobald Sie etwas versuchen wie std::toupper( 'ß' ) または std::tolower( '' ) en tout Kodierung, sind Sie in Schwierigkeiten. Denn 1), der Standard arbeitet immer nur mit einem Zeichen auf einmal, so dass es einfach nicht drehen kann ß en SS wie es richtig wäre. Und 2), der Standard arbeitet immer nur mit einem Zeichen zur gleichen Zeit, so dass er nicht entscheiden kann, ob in der Mitte eines Wortes steht (wobei wäre korrekt), oder am Ende ( ). Ein anderes Beispiel wäre std::tolower( 'I' ) was zu unterschiedlichen Ergebnissen führen sollte je nach Gebietsschema -- praktisch überall, wo man es erwarten würde i aber in der Türkei (LATIN SMALL LETTER DOTLESS I) ist die richtige Antwort (was wiederum mehr als ein Byte in UTF-8-Kodierung ist).

Also, tout Groß-/Kleinschreibung, die jeweils ein Zeichen bearbeitet, oder noch schlimmer, eine Byte zu einer Zeit, ist absichtlich unterbrochen. Dazu gehören alle std:: Varianten, die zu diesem Zeitpunkt existieren.

Dann gibt es noch den Punkt, dass die Standardbibliothek, für das, was sie ist fähig ist, hängt davon ab, welche Lokalitäten unterstützt auf dem Rechner, auf dem Ihre Software läuft... und was tun Sie, wenn Ihr Zielgebietsschema zu den nicht unterstützten auf dem Rechner Ihres Kunden gehört?

Was Sie also sind realmente Wir suchen eine String-Klasse, die mit all dem richtig umgehen kann, und das ist ノット einer der std::basic_string<> Varianten .

(C++11 Anmerkung: std::u16string y std::u32string son besser , aber immer noch nicht perfekt. C++20 gebracht std::u8string aber diese spezifizieren lediglich die Kodierung . In vielerlei Hinsicht sind sie immer noch nicht mit den Unicode-Mechanismen vertraut, wie Normalisierung, Kollationierung, ...)

Während Boost siehe nett, API-mäßig ist Boost.Locale im Grunde ein Wrapper um INTENSIVSTATION . Wenn Boost ist zusammengestellt mit ICU-Unterstützung... wenn nicht, ist Boost.Locale auf die für die Standardbibliothek kompilierte Locale-Unterstützung beschränkt.

Und glauben Sie mir, unter Die Kompilierung von Boost mit ICU kann manchmal eine echte Qual sein. (Es gibt keine vorkompilierten Binärdateien für Windows, die ICU enthalten, also müssen Sie sie zusammen mit Ihrer Anwendung bereitstellen, und dass öffnet ein ganz neues Wespennest...)

Ich persönlich würde daher empfehlen, die volle Unicode-Unterstützung direkt aus erster Hand 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

Dies ergibt:

Beachten Sie, dass die <-> Umwandlung in der Mitte des Wortes und die <-> Umwandlung am Ende des Wortes erfolgt. Keine <algorithm> -basierte Lösung kann Ihnen das bieten.

39voto

incises Punkte 995

Bei Verwendung 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);
}

35voto

Gilson PJ Punkte 3256

Ein anderer Ansatz mit einer bereichsbasierten for-Schleife mit Referenzvariable

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

cout<<test<<endl;

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X