Wie könnte man einen String in Großbuchstaben umwandeln? Die Beispiele, die ich beim Googeln gefunden habe, müssen sich nur mit Zeichen befassen.
268
Boost-String-Algorithmen:
#include <boost/algorithm/string.hpp>
#include <string>
std::string str = "Hello World";
boost::to_upper(str);
std::string newstr = boost::to_upper_copy<std::string>("Hello World");
::toupper
höchstwahrscheinlich ASCII angenommen wird.std::string newstr(boost::to_upper_copy<std::string>("Hello World"));
quelle
toupper()
kann als Makro implementiert werden. Dies kann ein Problem verursachen.toupper
. Irgendwelche Ideen?Kurze Lösung mit C ++ 11 und toupper ().
quelle
c
vomconst char
Typ (vonauto
)? Wenn ja, können Sie es (aufgrund einesconst
Teils) nicht dem zuweisen , was von zurückgegeben wirdtoupper(c)
.c
muss gegossen werden,unsigned char
damit dies korrigiert wird.Hinweis: Einige Probleme mit der Top-Lösung:
Dies bedeutet, dass die
cctype
Mitglieder möglicherweise Makros sind, die für den direkten Verbrauch in Standardalgorithmen nicht geeignet sind.Ein weiteres Problem mit demselben Beispiel besteht darin, dass das Argument nicht umgewandelt oder überprüft wird, dass dies nicht negativ ist. Dies ist besonders gefährlich für Systeme, in denen die Ebene
char
signiert ist. (Der Grund dafür ist: Wenn dies als Makro implementiert ist, wird wahrscheinlich eine Nachschlagetabelle verwendet und Ihr Argument wird in diese Tabelle indiziert. Ein negativer Index gibt Ihnen UB.)quelle
Dieses Problem ist mit SIMD für den ASCII-Zeichensatz vektorisierbar .
Beschleunigungsvergleiche:
Vorläufiger Test mit x86-64 gcc 5.2
-O3 -march=native
auf einem Core2Duo (Merom). Dieselbe Zeichenfolge mit 120 Zeichen (gemischtes ASCII in Klein- und Nicht-Kleinbuchstaben), die 40 Millionen Mal in eine Schleife konvertiert wurde (ohne Inlining zwischen Dateien, sodass der Compiler nichts davon optimieren oder aus der Schleife herausheben kann). Gleiche Quell- und Zielpuffer, also kein Malloc-Overhead oder Speicher- / Cache-Effekte: Daten sind die ganze Zeit im L1-Cache heiß und wir sind rein CPU-gebunden.boost::to_upper_copy<char*, std::string>()
: 198,0 s . Ja, Boost 1.58 unter Ubuntu 15.10 ist wirklich so langsam. Ich habe den ASM in einem Debugger profiliert und in einem Schritt ausgeführt, und es ist wirklich sehr, sehr schlecht: Es gibt einen dynamic_cast einer Gebietsschemavariablen pro Charakter !!! (dynamic_cast nimmt mehrere Aufrufe von strcmp entgegen). Das passiert mitLANG=C
und mitLANG=en_CA.UTF-8
.Ich habe keinen anderen RangeT als std :: string getestet. Vielleicht ist die andere Form
to_upper_copy
besser optimiert, aber ich denke , es wird immernew
/malloc
Platz für die Kopie, so dass es schwieriger zu testen ist. Vielleicht unterscheidet sich etwas, das ich getan habe, von einem normalen Anwendungsfall, und vielleicht kann normalerweise gestopptes g ++ das Gebietsschema-Setup-Zeug aus der Zeichenschleife herausheben. Meine Schleife, die von a lieststd::string
und in a schreibt,char dstbuf[4096]
macht zum Testen Sinn.Schleifenaufruf glibc
toupper
: 6.67s (dasint
Ergebnis wird jedoch nicht auf potenzielle Multi-Byte-UTF-8 überprüft . Dies ist für Türkisch wichtig.)cmov
, wobei die Tabelle in L1 sowieso heiß ist.Siehe auch diese Frage
toupper()
zur Langsamkeit unter Windows, wenn ein Gebietsschema festgelegt ist .Ich war schockiert, dass Boost eine Größenordnung langsamer ist als die anderen Optionen. Ich überprüfte noch einmal, ob ich
-O3
aktiviert hatte, und trat sogar in einem Schritt auf den Asm, um zu sehen, was er tat. Mit clang ++ 3.8 ist es fast genau die gleiche Geschwindigkeit. Es hat einen enormen Overhead innerhalb der Zeichenschleife. Dasperf record
/report
Ergebnis (für dascycles
Perf-Event) ist:Autovektorisierung
Gcc und clang werden Schleifen nur dann automatisch vektorisieren, wenn die Iterationszahl vor der Schleife bekannt ist. (dh Suchschleifen wie die Plain-C-Implementierung von
strlen
werden nicht automatisch synchronisiert.)Daher erhalten wir für Zeichenfolgen, die klein genug sind, um in den Cache zu passen, eine erhebliche Beschleunigung für Zeichenfolgen mit einer Länge von ~ 128 Zeichen
strlen
. Dies ist für Zeichenfolgen mit expliziter Länge (wie C ++std::string
) nicht erforderlich .Jede anständige libc hat eine Effizienz
strlen
, die viel schneller ist als das Schleifen eines Bytes, sodass separate vektorisierte Strlen- und Toupper-Schleifen schneller sind.Baseline: Eine Schleife, die im laufenden Betrieb nach einer endenden 0 sucht.
Zeiten für 40 Millionen Iterationen auf einem Core2 (Merom) 2,4 GHz. gcc 5.2
-O3 -march=native
. (Ubuntu 15.10).dst != src
(also machen wir eine Kopie), aber sie überlappen sich nicht (und sind nicht in der Nähe). Beide sind ausgerichtet.Einige Ergebnisse sind bei Clang etwas anders.
Die Microbenchmark-Schleife, die die Funktion aufruft, befindet sich in einer separaten Datei. Andernfalls wird es inline und
strlen()
aus der Schleife gehoben , und es läuft dramatisch schneller, insb. für 16 Zeichenfolgen (0,187 s).Dies hat den Hauptvorteil, dass gcc es für jede Architektur automatisch vektorisieren kann, aber den Hauptnachteil, dass es für den normalerweise üblichen Fall kleiner Zeichenfolgen langsamer ist.
Es gibt also große Beschleunigungen, aber die automatische Vektorisierung von Compilern macht keinen großartigen Code, insb. zur Bereinigung der letzten bis zu 15 Zeichen.
Manuelle Vektorisierung mit SSE-Intrinsics:
Basierend auf meiner Case-Flip-Funktion , die den Fall jedes alphabetischen Zeichens invertiert. Es nutzt den "vorzeichenlosen Vergleichstrick", bei dem Sie
low < a && a <= high
einen einzelnen vorzeichenlosen Vergleich durch Bereichsverschiebung durchführen können, sodass jeder Wert, der kleiner als ist,low
auf einen Wert umbrochen wird, der größer als isthigh
. (Dies funktioniert, wennlow
undhigh
nicht zu weit voneinander entfernt.)SSE hat nur einen vorzeichenbehafteten Vergleich größer, aber wir können den Trick "vorzeichenloser Vergleich" weiterhin verwenden, indem wir den Bereich an den unteren Rand des vorzeichenbehafteten Bereichs verschieben: Subtrahieren Sie 'a' + 128, sodass die alphabetischen Zeichen zwischen -128 und -128 liegen +25 (-128 + 'z' - 'a')
Beachten Sie, dass das Addieren von 128 und das Subtrahieren von 128 für 8-Bit-Ganzzahlen dasselbe sind. Es gibt keinen Ort, an den der Carry gehen kann, also ist es nur xor (Carryless Add), das das hohe Bit umdreht.
Angesichts dieser Funktion, die für einen Vektor funktioniert, können wir sie in einer Schleife aufrufen, um eine ganze Zeichenfolge zu verarbeiten. Da wir bereits auf SSE2 abzielen, können wir gleichzeitig eine vektorisierte Überprüfung des String-Endes durchführen.
Wir können auch viel besser für die "Bereinigung" der letzten bis zu 15 Bytes tun, die nach dem Ausführen von Vektoren von 16B übrig bleiben: Das obere Gehäuse ist idempotent, daher ist die erneute Verarbeitung einiger Eingabebytes in Ordnung. Wir laden die letzten 16B der Quelle nicht ausgerichtet und speichern sie im Zielpuffer, der den letzten 16B-Speicher der Schleife überlappt.
Dies funktioniert nur dann nicht, wenn die gesamte Zeichenfolge unter 16B liegt: Auch wenn
dst=src
nicht-atomares Lesen, Ändern, Schreiben nicht das Gleiche ist, als würden einige Bytes überhaupt nicht berührt, und kann Multithread-Code beschädigen.Wir haben eine Skalarschleife dafür und auch um uns
src
auszurichten. Da wir nicht wissen, wo sich die abschließende 0 befindet, wird möglicherweise eine nicht ausgerichtete Last vonsrc
auf die nächste Seite und den Segfault übertragen. Wenn wir Bytes in einem ausgerichteten 16B-Block benötigen, ist es immer sicher, den gesamten ausgerichteten 16B-Block zu laden.Vollständige Quelle: in einem Github-Kern .
Zeiten für 40 Millionen Iterationen auf einem Core2 (Merom) 2,4 GHz. gcc 5.2
-O3 -march=native
. (Ubuntu 15.10).dst != src
(also machen wir eine Kopie), aber sie überlappen sich nicht (und sind nicht in der Nähe). Beide sind ausgerichtet.(Tatsächlich zeitgesteuert mit
_mm_store
in der Schleife, nicht_mm_storeu
, da storeu auf Merom langsamer ist, selbst wenn die Adresse ausgerichtet ist. Es ist in Nehalem und später in Ordnung. Ich habe den Code vorerst auch unverändert gelassen, anstatt den Fehler beim Kopieren zu beheben die abschließende 0 in einigen Fällen, weil ich nicht alles neu einstellen möchte.)Für kurze Strings, die länger als 16B sind, ist dies dramatisch schneller als automatisch vektorisiert. Längen von weniger als einer Vektorbreite sind kein Problem. Sie können aufgrund eines Standes für die Weiterleitung von Geschäften ein Problem beim Betrieb vor Ort darstellen. (Beachten Sie jedoch, dass es immer noch in Ordnung ist, unsere eigene Ausgabe anstelle der ursprünglichen Eingabe zu verarbeiten, da toupper idempotent ist.)
Es gibt viel Spielraum, dies für verschiedene Anwendungsfälle zu optimieren, abhängig von den Anforderungen des umgebenden Codes und der Zielmikroarchitektur. Es ist schwierig, den Compiler dazu zu bringen, netten Code für den Bereinigungsteil auszugeben. Die Verwendung
ffs(3)
(die auf x86 zu bsf oder tzcnt kompiliert wird) scheint gut zu sein, aber offensichtlich muss dieses Bit überdacht werden, da ich nach dem Schreiben des größten Teils dieser Antwort einen Fehler festgestellt habe (siehe die FIXME-Kommentare).Vektorbeschleunigungen für noch kleinere Zeichenfolgen können mit
movq
odermovd
Laden / Speichern erhalten werden. Passen Sie nach Bedarf Ihren Anwendungsfall an.UTF-8:
Wir können erkennen, wann unser Vektor Bytes mit gesetztem High-Bit hat, und in diesem Fall auf eine skalare utf-8-fähige Schleife für diesen Vektor zurückgreifen. Der
dst
Punkt kann um einen anderen Betrag als dersrc
Zeiger vorrücken , aber sobald wir zu einem ausgerichtetensrc
Zeiger zurückkehren, führen wir immer noch nur nicht ausgerichtete Vektorspeicher durchdst
.Für Text, der UTF-8 ist, aber hauptsächlich aus der ASCII-Teilmenge von UTF-8 besteht, kann dies gut sein: hohe Leistung im allgemeinen Fall mit korrektem Verhalten in allen Fällen. Wenn es viele Nicht-ASCII-Dateien gibt, ist dies wahrscheinlich schlimmer, als die ganze Zeit in der skalaren UTF-8-fähigen Schleife zu bleiben.
Englisch auf Kosten anderer Sprachen schneller zu machen, ist keine zukunftssichere Entscheidung, wenn der Nachteil erheblich ist.
Gebietsschema-bewusst:
In der türkischen locale (
tr_TR
), das richtige Ergebnis austoupper('i')
ist'İ'
(U0130) nicht'I'
(plain ASCII). Siehe Martin Bonners Kommentare zu einer Fragetolower()
, wie man unter Windows langsam ist.Wir können dort auch nach einer Ausnahmeliste suchen und auf Skalar zurückgreifen, wie bei Multi-Byte-UTF8-Eingabezeichen.
Mit dieser Komplexität kann SSE4.2
PCMPISTRM
oder ähnliches möglicherweise viele unserer Überprüfungen auf einmal durchführen.quelle
Haben Sie ASCII- oder internationale Zeichen in Zeichenfolgen?
Wenn es der letztere Fall ist, ist "Großbuchstaben" nicht so einfach und hängt vom verwendeten Alphabet ab. Es gibt Zweikammer- und Einkammer-Alphabete. Nur Zweikammeralphabete haben unterschiedliche Zeichen für Groß- und Kleinschreibung. Es gibt auch zusammengesetzte Zeichen wie den lateinischen Großbuchstaben 'DZ' (\ u01F1 'DZ'), die die sogenannte Groß- und Kleinschreibung verwenden . Dies bedeutet, dass nur das erste Zeichen (D) geändert wird.
Ich schlage vor, Sie untersuchen die Intensivstation und den Unterschied zwischen einfachen und vollständigen Fallzuordnungen. Dies könnte helfen:
http://userguide.icu-project.org/transforms/casemappings
quelle
Oder,
quelle
**
nach den Parametern der ersten Lösung?**
ein Tippfehler ist, der von dem Versuch übrig geblieben ist, fette Schrift in der Codesyntax zu verwenden.toupper
er mit negativen Zahlen aufgerufen wird.Folgendes funktioniert für mich.
quelle
toupper
er mit negativen Zahlen aufgerufen wird.Verwenden Sie ein Lambda.
quelle
Das schnellere, wenn Sie nur ASCII-Zeichen verwenden :
Bitte beachten Sie, dass dieser Code schneller ausgeführt wird, aber nur unter ASCII funktioniert und keine "abstrakte" Lösung ist.
Wenn Sie UNICODE-Lösungen oder konventionellere und abstraktere Lösungen benötigen, suchen Sie nach anderen Antworten und arbeiten Sie mit Methoden von C ++ - Zeichenfolgen.
quelle
C++
, aber SieC
haben hier eine Antwort geschrieben. (Ich bin nicht einer der Downvoter.)'
?Solange Sie nur mit ASCII gut umgehen können und einen gültigen Zeiger auf den RW-Speicher bereitstellen können, gibt es in C einen einfachen und sehr effektiven Einzeiler:
Dies ist besonders gut für einfache Zeichenfolgen wie ASCII-Bezeichner geeignet, die Sie in die gleiche Groß- und Kleinschreibung normalisieren möchten. Sie können dann den Puffer verwenden, um eine std: string-Instanz zu erstellen.
quelle
quelle
for (size_t i = 0 ...
. Es gibt auch keinen guten Grund, das Lesen so schwer zu machen. Dadurch wird auch zuerst die Zeichenfolge kopiert und dann eine Schleife durchlaufen. @ Lukes Antwort ist in mancher Hinsicht besser, außer dass'a'
Zeichenkonstanten nicht ausgenutzt werden.Dies ist besser als alle Antworten, die die globale Toupper-Funktion verwenden, und ist vermutlich das, was boost :: to_upper darunter tut.
Dies liegt daran, dass :: toupper bei jedem Aufruf das Gebietsschema nachschlagen muss - da es möglicherweise von einem anderen Thread geändert wurde -, während hier nur der Aufruf von locale () diese Strafe hat. Zum Nachschlagen des Gebietsschemas gehört im Allgemeinen das Sperren.
Dies funktioniert auch mit C ++ 98, nachdem Sie das Auto ersetzt, die neue nicht-const str.data () verwendet und ein Leerzeichen hinzugefügt haben, um das Schließen der Vorlage (">>" bis ">>") wie folgt zu unterbrechen:
quelle
quelle
reserve
undback_inserter
(damit die Zeichenfolge nur einmal kopiert wird).inline std::string to_lower(const std::string &s) { std::string result; result.reserve(s.size()); std::transform(s.begin(), s.end(), std::back_inserter( result ), static_cast<int(*)(int)>(std::tolower)); return result; }
quelle
toupper
er mit negativen Zahlen aufgerufen wird.Probieren Sie die
toupper()
Funktion (#include <ctype.h>
) aus. Zeichen werden als Argumente akzeptiert, Zeichenfolgen bestehen aus Zeichen, sodass Sie jedes einzelne Zeichen durchlaufen müssen, das zusammen die Zeichenfolge umfasstquelle
toupper
er mit negativen Zahlen aufgerufen wird. Du hättest die notwendige Besetzung erwähnen sollenunsigned char
.Hier ist der neueste Code mit C ++ 11
quelle
toupper
er mit negativen Zahlen aufgerufen wird.Verwenden von Boost.Text, das für Unicode-Text funktioniert
quelle
Die Antwort von @dirkgently ist sehr inspirierend, aber ich möchte dies aufgrund der unten gezeigten Besorgnis hervorheben.
Die korrekte Verwendung von
std::toupper
sollte sein:Ausgabe:
quelle
Ich bin mir nicht sicher, ob eine Funktion integriert ist. Versuche dies:
Fügen Sie entweder die Bibliotheken ctype.h ODER cctype sowie stdlib.h als Teil der Präprozessor-Direktiven hinzu.
quelle
toupper
er mit negativen Zahlen aufgerufen wird.Meine Lösung (6. Bit für Alpha löschen):
quelle
toupper
er mit negativen Zahlen aufgerufen wird.ALLE diese Lösungen auf dieser Seite sind schwieriger als nötig.
Mach das
RegName
ist deinstring
. Holen Sie sich Ihre Stringgröße nichtstring.size()
als eigentlicher Tester verwenden, sehr chaotisch und kann Probleme verursachen. dann. die grundlegendstefor
Schleife.Denken Sie daran, dass die Zeichenfolgengröße auch das Trennzeichen zurückgibt. Verwenden Sie daher <und nicht <= in Ihrem Schleifentest.
Die Ausgabe lautet: eine Zeichenfolge, die konvertiert werden soll
quelle
tolower
Schleifen, und die meisten von ihnen verwenden Standardnamen für Schleifenvariableni
, nicht die seltsamenforLoop
.Ohne Verwendung von Bibliotheken:
quelle
Wenn Sie sich nur mit 8-Bit-Zeichen befassen (von denen alle anderen Antworten außer Milan Babuškov ebenfalls ausgehen), können Sie die schnellste Geschwindigkeit erzielen, indem Sie zur Kompilierungszeit mithilfe der Metaprogrammierung eine Nachschlagetabelle erstellen. Auf ideone.com läuft dies 7x schneller als die Bibliotheksfunktion und 3x schneller als eine handgeschriebene Version ( http://ideone.com/sb1Rup ). Es kann auch durch Merkmale ohne Verlangsamung angepasst werden.
mit Anwendungsfall:
Für eine ausführliche (viele Seiten) Beschreibung der Funktionsweise kann ich mein Blog schamlos einbinden: http://metaporky.blogspot.de/2014/07/part-4-generating-look-up-tables-at.html
quelle
quelle
Diese c ++ - Funktion gibt immer die Zeichenfolge in Großbuchstaben zurück ...
quelle
Ich benutze diese Lösung. Ich weiß, dass Sie diesen Datenbereich nicht ändern sollen ... aber ich denke, das ist hauptsächlich für Pufferüberlauffehler und Nullzeichen gedacht ... die Dinge im oberen Gehäuse sind nicht die gleichen.
quelle
I know you're not supposed to modify that data area
- Welchen Datenbereich sollen Sie nicht ändern?str[i] = toupper(str[i]);
vollkommen in Ordnung ersetzt werden ( na ja , nicht vollkommen in Ordnung, aber sie behebt die meisten Fehler).