Die Magie der Groß-/Kleinschreibung: Warum eine einfache Frage zum Kult wurde
Stellen Sie sich vor: Sie schreiben ein Programm in C++, das Benutzernamen vergleichen soll. Aber "John", "john" und "JOHN" sind dieselbe Person, oder? Oder Sie parsen eine Konfigurationsdatei, in der Schlüssel in beliebiger Groß-/Kleinschreibung geschrieben sein können. Und dann stellen Sie eine scheinbar triviale Frage: "Wie wandle ich std::string in Kleinbuchstaben um?"
Diese Frage auf Stack Overflow hat Hunderttausende von Aufrufen und Dutzende von Antworten gesammelt. Warum? Weil die Umwandlung eines Strings in Kleinbuchstaben in C++ nicht einfach der Aufruf einer einzigen Funktion ist. Es ist eine ganze Welt, in der Locale, Kodierungen, Leistung und die Standardbibliothek aufeinandertreffen. In diesem Artikel werfen wir einen Blick unter die Haube und verstehen, wie diese Operation tatsächlich funktioniert.
Die klassische Antwort: std::transform + std::tolower
Wenn Sie nach "C++ string lower case example" gegoogelt haben, haben Sie sicherlich dieses Codefragment gesehen:
#include <algorithm>#include <cctype>#include <string>
std::string to_lower_case(const std::string& str) { std::string result = str; std::transform(result.begin(), result.end(), result.begin(), [](unsigned char c) { return std::tolower(c); }); return result;}Dieser Code funktioniert, aber er ist nur die Spitze des Eisbergs. Lassen Sie uns ihn im Detail analysieren.
Warum unsigned char und nicht char?
Der häufigste Fehler von Anfängern ist die Übergabe eines Werts vom Typ char an std::tolower. Der Grund ist, dass std::tolower aus <cctype> einen int akzeptiert und erwartet, dass der Wert als unsigned char darstellbar ist (oder gleich EOF ist). Wenn Sie einen negativen char-Wert übergeben (und in einigen Implementierungen ist char vorzeichenbehaftet), ist das Verhalten undefiniert (undefined behavior).
Deshalb verwenden wir ein Lambda mit unsigned char:
[](unsigned char c) { return std::tolower(c); }Dies stellt sicher, dass wir den Bereich der zulässigen Werte nicht verlassen.
Alternative: for-Schleife
Einige Entwickler bevorzugen einen expliziteren Ansatz:
std::string to_lower_case_for(const std::string& str) { std::string result; result.reserve(str.size()); for (char ch : str) { result.push_back(std::tolower(static_cast<unsigned char>(ch))); } return result;}Diese Variante ist etwas weniger elegant, aber inhaltlich völlig gleichwertig. Sie zeigt deutlich, dass wir jedes Zeichen durchlaufen.
Wie es unter der Haube funktioniert: std::tolower
Die Funktion std::tolower ist keine Magie. In den meisten Implementierungen verwendet sie eine Nachschlagetabelle (lookup table). Für jeden möglichen Wert von unsigned char (0-255) ist in dieser Tabelle der entsprechende Kleinbuchstabenwert vorab gespeichert. Somit ist der Aufruf von std::tolower('A') einfach ein schneller Zugriff auf ein Array-Element mit dem Index 65 (Code des Zeichens 'A'), der 97 (Code von 'a') zurückgibt.
Es gibt jedoch eine Nuance: die Locale. Standardmäßig verwendet std::tolower die "C"-Locale, die nur mit ASCII-Zeichen arbeitet. Wenn Sie mit russischen Buchstaben, deutschen Umlauten (ä, ö, ü) oder französischen Akzenten (é, è) arbeiten, hilft Ihnen das klassische std::tolower nicht weiter – es gibt einfach das ursprüngliche Zeichen unverändert zurück.
Das Problem der Locale: Wenn eine Funktion nicht ausreicht
Für die Arbeit mit Multibyte-Kodierungen und nationalen Alphabeten müssen Sie std::tolower mit dem Locale-Parameter verwenden:
#include <locale>
std::string to_lower_cas