So konvertieren Sie eine Instanz von std::string in Kleinbuchstaben

Lesezeit: 10 Minuten

So konvertieren Sie eine Instanz von stdstring in Kleinbuchstaben
Konrad

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?

  • 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

So konvertieren Sie eine Instanz von stdstring in Kleinbuchstaben
Stefan Mai

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.

  • (Alt mag es sein, die fraglichen Algorithmen haben sich wenig geändert) @Stefan Mai: Welche Art von “ganzer Menge Overhead” gibt es beim Aufrufen von STL-Algorithmen? Die Funktionen sind ziemlich schlank (dh einfache for-Schleifen) und oft inliniert, da Sie selten viele Aufrufe derselben Funktion mit denselben Template-Parametern in derselben Kompiliereinheit haben.

    – gleich

    11. November 2011 um 22:14 Uhr

  • Jedes Mal, wenn Sie davon ausgehen, dass Zeichen ASCII sind, tötet Gott ein Kätzchen. 🙁

    – Lappen

    10. Februar 2014 um 20:49 Uhr

  • Ihr erstes Beispiel hat möglicherweise undefiniertes Verhalten (Vorbeigehen char zu ::tolower(int).) Sie müssen sicherstellen, dass Sie keinen negativen Wert übergeben.

    – Juanchopanza

    29. Mai 2014 um 17:30 Uhr


  • -1 diese Verwendung von ::tolower kann durchaus abstürzen, es ist UB für Nicht-ASCII-Eingabe.

    – Prost und hth. – Alf

    29. Mai 2014 um 17:34 Uhr

  • Das :: wird vor tolower benötigt, um anzuzeigen, dass es sich im äußersten Namensraum befindet. Wenn Sie diesen Code in einem anderen Namensraum verwenden, gibt es möglicherweise eine andere (möglicherweise nicht zusammenhängende) Definition von tolower, die am Ende bevorzugt ohne das :: ausgewählt würde.

    – Karl Ofria

    30. Juli 2016 um 16:43 Uhr

1647185417 725 So konvertieren Sie eine Instanz von stdstring in Kleinbuchstaben
rauben

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

  • Schlägt für Nicht-ASCII-7 fehl.

    – DevSolar

    27. Februar 2015 um 9:28 Uhr

  • Das ist ziemlich langsam, siehe diesen Benchmark: godbolt.org/z/neM5jsva1

    – prähistorischer Pinguin

    29. Juni 2021 um 10:31 Uhr


  • @prehistoricpenguin langsam? Nun, langsam ist es, Code zu debuggen, weil Ihre eigene Implementierung einen Fehler hat, weil es komplizierter war, als nur die Boost-Bibliothek aufzurufen 😉 Wenn der Code kritisch ist, wie er oft aufgerufen wird und einen Engpass darstellt, dann kann es sein Es lohnt sich, über Langsamkeit nachzudenken

    – Mayou36

    12. Februar um 12:00 Uhr

1647185418 104 So konvertieren Sie eine Instanz von stdstring in Kleinbuchstaben
DevSolar

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 isondern 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::u8stringaber 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.

  • Dies ist im allgemeinen Fall die richtige Antwort. Der Standard gibt nichts für den Umgang mit irgendetwas außer “ASCII” vor, außer Lügen und Täuschung. Es macht dich Überlegen Sie können vielleicht mit UTF-16 umgehen, aber Sie können nicht. Wie diese Antwort sagt, können Sie nicht die richtige Zeichenlänge (nicht Bytelänge) einer UTF-16-Zeichenfolge erhalten, ohne Ihre eigene Unicode-Behandlung durchzuführen. Wenn Sie sich mit echtem Text befassen müssen, verwenden Sie ICU. Danke, @DevSolar

    – lmat – Wiedereinsetzung von Monica

    25. März 2015 um 14:00 Uhr

  • Ist ICU standardmäßig auf Ubuntu/Windows verfügbar oder muss es separat installiert werden? Wie wäre es auch mit dieser Antwort: stackoverflow.com/a/35075839/207661?

    – Shital Shah

    11. Mai 2016 um 19:00 Uhr


  • icu::UnicodeString::length() lügt Sie technisch gesehen auch an (wenn auch seltener), da es die Anzahl der 16-Bit-Codeeinheiten und nicht die Anzahl der Codepunkte meldet. 😉

    – Masaer

    15. Juni 2017 um 2:17 Uhr

  • @masaers: Um ganz fair zu sein, ist die Anzahl der Codepunkte bei Dingen wie dem Kombinieren von Zeichen, Null-Breite-Joinern und Rechts-nach-Links-Markierungen ziemlich bedeutungslos. Ich werde diesen Hinweis entfernen.

    – DevSolar

    15. Juni 2017 um 5:26 Uhr

  • @DevSolar Einverstanden! Das Konzept der Länge ist für Text ziemlich bedeutungslos (wir könnten der Liste der Übeltäter Ligaturen hinzufügen). Da die Menschen jedoch daran gewöhnt sind, dass Tabulatoren und Steuerzeichen eine Längeneinheit einnehmen, wären Codepunkte das intuitivere Maß. Oh, und danke für die richtige Antwort, traurig, es so weit unten zu sehen 🙁

    – Masaer

    15. Juni 2017 um 6:51 Uhr

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

Wenn der String 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. Sehen http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/conversions.html

  • Ein funktionierendes Beispiel?

    – Velkan

    2. Januar um 15:43 Uhr

1647185418 346 So konvertieren Sie eine Instanz von stdstring in Kleinbuchstaben
Gilson PJ

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;

  • Ein funktionierendes Beispiel?

    – Velkan

    2. Januar um 15:43 Uhr

1647185419 392 So konvertieren Sie eine Instanz von stdstring in Kleinbuchstaben
Benutzer2218467

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

  • Dies hat Ä in ä für mich nicht geändert

    – Reinfan

    23. Januar 2016 um 16:12 Uhr

  • Könnte hier auch einen Back-Inserter-Iterator anstelle einer manuellen Größenänderung verwenden.

    – Chili

    24. April 2017 um 1:57 Uhr

998250cookie-checkSo konvertieren Sie eine Instanz von std::string in Kleinbuchstaben

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy