Ich möchte a std::string
in Kleinbuchstaben umwandeln . Ich bin mir der Funktion bewusst tolower()
, aber in der Vergangenheit hatte ich Probleme mit dieser Funktion und sie ist sowieso kaum ideal, da die Verwendung mit a std::string
das Durchlaufen jedes Zeichens erfordern würde.
Gibt es eine Alternative, die 100% der Zeit funktioniert?
c++
string
c++-standard-library
tolower
Konrad
quelle
quelle
Antworten:
Angepasst an nicht so häufig gestellte Fragen :
Du wirst wirklich nicht davonkommen, ohne jeden Charakter zu durchlaufen. Es gibt keine Möglichkeit festzustellen, ob das Zeichen sonst in Klein- oder Großbuchstaben geschrieben ist.
Wenn Sie es wirklich hassen
tolower()
, finden Sie hier eine spezielle ASCII-Alternative, die Sie nicht empfehlen:Beachten Sie, dass
tolower()
nur eine Ersetzung pro Einzelbytezeichen möglich ist, was für viele Skripte nicht geeignet ist, insbesondere wenn eine Mehrbyte-Codierung wie UTF-8 verwendet wird.quelle
char
an::tolower(int)
). Sie müssen sicherstellen, dass Sie keinen negativen Wert übergeben.::tolower
kann durchaus abstürzen, es ist UB für Nicht-ASCII-Eingabe.Boost bietet hierfür einen String-Algorithmus :
Oder für Nicht-In-Place :
quelle
to_lower_copy
tl; dr
Verwenden Sie die ICU-Bibliothek . Wenn Sie dies nicht tun, wird Ihre Konvertierungsroutine in Fällen, in denen Sie wahrscheinlich gar nicht wissen, dass sie existieren, stillschweigend unterbrochen.
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? Weiß das, was auch immer Sie zum Konvertieren von Groß- und Kleinbuchstaben verwenden? (Oder scheitert es kläglich für Charaktere vorbei0x7f
?)Wenn Sie UTF-8 (die einzig vernünftige Wahl unter den 8-Bit-Codierungen) mit
std::string
als Container verwenden, täuschen Sie sich bereits in der Annahme, dass Sie immer noch die Kontrolle über die Dinge haben, da Sie eine Multibyte-Zeichenfolge in einem Container speichern das ist sich des Multibyte-Konzepts nicht bewusst. Sogar etwas so Einfaches wie.substr()
eine tickende Zeitbombe. (Da das Aufteilen einer Multibyte-Sequenz zu einer ungültigen (Unter-) Zeichenfolge führt.)Und sobald Sie etwas versuchen
std::toupper( 'ß' )
, in irgendeiner Codierung, sind Sie in großen Schwierigkeiten. (Weil es mit der Standardbibliothek, die nur ein Ergebniszeichen liefern kann , das"SS"
hier nicht benötigt wird , einfach nicht "richtig" ist.) [1] Ein anderes Beispiel wärestd::tolower( 'I' )
, das je nach Gebietsschema unterschiedliche Ergebnisse liefern sollte . In Deutschland'i'
wäre das richtig; In der Türkei ist'ı'
(LATIN SMALL LETTER DOTLESS I) das erwartete Ergebnis (das wiederum mehr als ein Byte in der UTF-8-Codierung beträgt). Ein weiteres Beispiel ist das griechische Sigma , Groß-'∑'
und Kleinschreibung'σ'
... außer am Ende eines Wortes, wo es sich befindet'ς'
.Also, jeder Fall Konvertierung , die auf einem Zeichen in einer Zeit arbeitet, oder noch schlimmer, ein Byte zu einem Zeitpunkt, wird durch Design gebrochen.
Dann gibt es den Punkt , dass die Standard - Bibliothek, für das, was es ist dazu in der Lage ist abhängig davon , welche Lokalisationen werden unterstützt auf der Maschine Ihre Software auf läuft ... und was tun Sie , wenn es nicht ist?
Also , was Sie wirklich suchen, ist ein String - Klasse , die mit all dies zu tun richtig fähig ist, und das ist nicht eine der
std::basic_string<>
Varianten .(C ++ 11 Hinweis:
std::u16string
undstd::u32string
sind besser , aber immer noch nicht perfekt. C ++ 20 gebrachtstd::u8string
, aber alles, was Sie tun, ist die Codierung anzugeben. In vielerlei Hinsicht bleiben sie immer noch unwissend über Unicode-Mechanik, wie Normalisierung, Kollatierung, .. .)Während Boost in Bezug auf die API gut aussieht , ist Boost.Locale im Grunde ein Wrapper um die Intensivstation . Wenn Boost mit ICU-Unterstützung kompiliert wird, ist Boost.Locale auf die für die Standardbibliothek kompilierte Gebietsschema-Unterstützung beschränkt.
Und glauben Sie mich, immer Boost zu kompilieren mit ICU manchmal einen echten Schmerzen sein kann. (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 ...)
Daher würde ich persönlich empfehlen, die volle Unicode-Unterstützung direkt aus dem Maul des Pferdes zu erhalten und die Intensivbibliothek direkt zu nutzen:
Kompilieren (in diesem Beispiel mit G ++):
Das gibt:
Beachten Sie, dass die Σ <-> σ-Konvertierung in der Mitte des Wortes und die Σ <-> ς-Konvertierung am Ende des Wortes. Keine
<algorithm>
basierende Lösung kann Ihnen das geben.[1] 2017 entschied der Rat für deutsche Rechtschreibung, dass "" "U + 1E9E LATIN CAPITAL LETTER SHARP S offiziell als Option neben der traditionellen" SS "-Konvertierung verwendet werden kann, um Unklarheiten zu vermeiden, z. B. in Pässen (bei denen Namen groß geschrieben werden) ). Mein schönes Beispiel, das durch die Entscheidung des Komitees überholt wurde ...
quelle
toupper
und arbeitentolower
immer noch an einzelnen Charakteren. Die String-Klasse hat immer noch keine Vorstellung von Normalisierung (z. B. ob ein "ü" als "u mit Diaeresis" oder "u + kombinierte Diaeresis" codiert ist) oder wo ein String getrennt werden kann oder nicht. Die Liste geht weiter. u8string ist (wie die anderen Standard-String-Klassen) für "Durchlaufen" geeignet. Wenn Sie jedoch Unicode verarbeiten möchten , benötigen Sie eine Intensivstation.Bei Verwendung einer bereichsbasierten for-Schleife von C ++ 11 wäre ein einfacherer Code:
quelle
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
quelle
Dies ist eine Folge der Antwort von Stefan Mai: Wenn Sie das Ergebnis der Konvertierung in eine andere Zeichenfolge einfügen möchten, müssen Sie den Speicherplatz vor dem Aufruf vorab zuweisen
std::transform
. Da STL transformierte Zeichen im Zieliterator speichert (bei jeder Iteration der Schleife inkrementiert), wird die Größe der Zielzeichenfolge nicht automatisch geändert, und Sie riskieren ein Stomping des Speichers.quelle
Ein anderer Ansatz, der eine auf einem Bereich basierende Schleife mit Referenzvariable verwendet
quelle
Soweit ich sehe, sind Boost-Bibliotheken in Bezug auf die Leistung wirklich schlecht. Ich habe ihre unordered_map auf STL getestet und sie war durchschnittlich dreimal langsamer (bester Fall 2, schlechtester zehnmal). Auch dieser Algorithmus sieht zu niedrig aus.
Der Unterschied ist so groß, dass ich mir sicher bin, dass jede Ergänzung, die Sie tun müssen,
tolower
um den Boost "für Ihre Bedürfnisse" gleichzusetzen, viel schneller als der Boost sein wird.Ich habe diese Tests auf einem Amazon EC2 durchgeführt, daher variierte die Leistung während des Tests, aber Sie haben immer noch die Idee.
-O2
machte es so:Quelle:
Ich denke, ich sollte die Tests auf einem dedizierten Computer durchführen, aber ich werde diesen EC2 verwenden, damit ich ihn nicht wirklich auf meinem Computer testen muss.
quelle
Der einfachste Weg, einen String in Kleinbuchstaben umzuwandeln, ohne sich um den Standard-Namespace zu kümmern, ist der folgende
1: Zeichenfolge mit / ohne Leerzeichen
2: Zeichenfolge ohne Leerzeichen
quelle
std::ctype::tolower()
aus der Standard-C ++ - Lokalisierungsbibliothek erledigt dies korrekt für Sie. Hier ist ein Beispiel, das aus der unteren Referenzseite extrahiert wurdequelle
const
? Das scheint es etwas chaotischer zu machen (z. B. sieht es nicht so aus, als könnten Sie es verwendenf.tolower()
), da Sie die Zeichen in eine neue Zeichenfolge einfügen müssen. Würden Sietransform()
und so etwasstd::bind1st( std::mem_fun() )
für den Betreiber verwenden?tolower
mitlocale
Parametern der implizite Aufruf vonuse_facet
ein Leistungsengpass zu sein scheint. Einer meiner Mitarbeiter hat eine Geschwindigkeitssteigerung von mehreren 100% erreicht, indem erboost::iequals
(was dieses Problem hat) durch eine Version ersetztuse_facet
hat, die nur einmal außerhalb der Schleife aufgerufen wird.Eine Alternative zu Boost ist POCO (pocoproject.org).
POCO bietet zwei Varianten:
"In Place" -Versionen haben immer "InPlace" im Namen.
Beide Versionen werden unten gezeigt:
quelle
Es gibt eine Möglichkeit, Großbuchstaben in Kleinbuchstaben umzuwandeln, OHNE Tests durchzuführen , und dies ist ziemlich einfach. Die Verwendung von clocale.h durch die Funktion / das Makro von isupper () sollte sich um Probleme in Bezug auf Ihren Standort kümmern. Wenn nicht, können Sie das UtoL [] jederzeit nach Herzenslust anpassen.
Da die Zeichen von C wirklich nur 8-Bit-Ints sind (wobei die breiten Zeichensätze im Moment ignoriert werden), können Sie ein 256-Byte-Array erstellen, das einen alternativen Zeichensatz enthält, und in der Konvertierungsfunktion die Zeichen in Ihrer Zeichenfolge als Indizes für die Zeichen verwenden Konvertierungsarray.
Geben Sie den Array-Mitgliedern in Großbuchstaben anstelle einer 1-zu-1-Zuordnung die BYTE-Int-Werte für die Kleinbuchstaben. Hier finden Sie möglicherweise islower () und isupper () .
Der Code sieht so aus ...
Dieser Ansatz ermöglicht es Ihnen gleichzeitig, alle anderen Zeichen, die Sie ändern möchten, neu zuzuordnen.
Dieser Ansatz hat einen großen Vorteil, wenn er auf modernen Prozessoren ausgeführt wird. Es ist nicht erforderlich, eine Verzweigungsvorhersage durchzuführen, da es keine If-Tests gibt, die eine Verzweigung umfassen. Dies speichert die Verzweigungsvorhersagelogik der CPU für andere Schleifen und verhindert tendenziell ein Abwürgen der Pipeline.
Einige hier erkennen diesen Ansatz möglicherweise als denselben an, der zum Konvertieren von EBCDIC in ASCII verwendet wird.
quelle
Da in keiner der Antworten die bevorstehende Ranges-Bibliothek erwähnt wurde, die seit C ++ 20 in der Standardbibliothek verfügbar ist und derzeit separat auf GitHub als verfügbar ist
range-v3
, möchte ich eine Möglichkeit hinzufügen, diese Konvertierung mit ihr durchzuführen.So ändern Sie die Zeichenfolge an Ort und Stelle:
So generieren Sie eine neue Zeichenfolge:
(Vergessen Sie nicht
#include <cctype>
und die erforderlichen Ranges-Header.)Hinweis: Die Verwendung
unsigned char
als Argument für das Lambda ist von cppreference inspiriert , in dem es heißt:quelle
Meine eigenen Vorlagenfunktionen, die Groß- / Kleinschreibung ausführen.
quelle
towlower
für breite Zeichen verwendet, das UTF-16 unterstützt.Hier ist eine Makrotechnik, wenn Sie etwas Einfaches wollen:
Beachten Sie jedoch, dass der Kommentar von @ AndreasSpindler zu dieser Antwort immer noch eine wichtige Überlegung ist, wenn Sie an etwas arbeiten, das nicht nur aus ASCII-Zeichen besteht.
quelle
void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
x
könnte ein gültiger Ausdruck sein, der zufällig korrekt kompiliert wird, aber aufgrund der Makros völlig falsche Ergebnisse liefert.Für weitere Informationen: http://www.cplusplus.com/reference/locale/tolower/
quelle
Nein
Es gibt mehrere Fragen, die Sie sich stellen müssen, bevor Sie eine Kleinbuchstabenmethode auswählen.
Sobald Sie Antworten auf diese Fragen haben, können Sie nach einer Lösung suchen, die Ihren Anforderungen entspricht. Es gibt keine Einheitsgröße, die für alle überall funktioniert!
quelle
Probieren Sie diese Funktion aus :)
quelle
Auf Microsoft-Plattformen können Sie die
strlwr
Funktionsfamilie verwenden: http://msdn.microsoft.com/en-us/library/hkxwh33z.aspxquelle
Code-Auszug
quelle
Verwenden Sie fplus :: to_lower_case ().
(fplus: https://github.com/Dobiasd/FunctionalPlus .
Suchen Sie nach 'to_lower_case' unter http://www.editgym.com/fplus-api-search/ )
quelle
Kopieren, da die Antwort nicht verbessert werden durfte. Danke SO
Erläuterung:
for(auto& c : test)
ist eine bereichsbasierte for-Schleife der Art :for (
range_declaration
:
range_expression
)
loop_statement
range_declaration
:auto& c
Hier wird der Auto- Bezeichner für den automatischen Typabzug verwendet. Der Typ wird also vom Variableninitialisierer abgezogen.
range_expression
:test
Der Bereich in diesem Fall sind die Zeichen der Zeichenfolge
test
.Die Zeichen der Zeichenfolge
test
stehen als Referenz innerhalb der for-Schleife durchc
.quelle
In C ++ sind keine Tolower- oder Toupper-Methoden für Zeichenfolgen implementiert, sie sind jedoch für char verfügbar. Man kann jedes Zeichen einer Zeichenfolge leicht lesen, in die gewünschte Groß- und Kleinschreibung konvertieren und wieder in eine Zeichenfolge einfügen. Ein Beispielcode ohne Verwendung einer Bibliothek eines Drittanbieters:
Für zeichenbasierte Operationen an Zeichenfolgen: Für jedes Zeichen in Zeichenfolgen
quelle
Dies könnte eine weitere einfache Version sein, um Großbuchstaben in Kleinbuchstaben umzuwandeln und umgekehrt. Ich habe die VS2017-Community-Version verwendet, um diesen Quellcode zu kompilieren.
Hinweis: Wenn Sonderzeichen vorhanden sind, müssen diese mithilfe der Bedingungsprüfung behandelt werden.
quelle
Ich habe versucht, std :: transform, alles was ich bekomme ist ein abscheulicher stl criptic Kompilierungsfehler, den nur Druiden von vor 200 Jahren verstehen können (kann nicht von zu Flibidi Flabidi Grippe konvertieren)
Dies funktioniert gut und kann leicht angepasst werden
quelle