Konvertieren Sie einen String in C ++ in Großbuchstaben

268

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.

OrangeAlmondSoap
quelle

Antworten:

205

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");
Tony Edgecombe
quelle
5
Dies hat auch den Vorteil von i18n, wo ::toupperhöchstwahrscheinlich ASCII angenommen wird.
Ben Straub
4
Ihre letzte Zeile wird nicht kompiliert - Sie müssen zu etwas wechseln wie:std::string newstr(boost::to_upper_copy<std::string>("Hello World"));
maxschlepzig
58
Dies sollte nicht die akzeptierte Antwort sein, da ein Boost erforderlich ist, oder der Titel sollte geändert werden.
Andrea
44
Ja, ich werde Boost nur für to_upper installieren ... ausgezeichnete Idee! </ sarcasm> :)
thang
12
Ich persönlich bin gegenüber Boost als Antwort auf "Wie mache ich x in C ++?" weil Boost überhaupt keine leichte Lösung ist. Es scheint, dass Sie sich entweder für Boost als Framework entscheiden (oder für ACE oder Qt oder Recusion ToolKit ++ oder ...) oder nicht. Ich würde lieber Sprachlösungen sehen.
JWM
485
#include <algorithm>
#include <string>

std::string str = "Hello World";
std::transform(str.begin(), str.end(),str.begin(), ::toupper);
Pierre
quelle
8
Eigentlich toupper()kann als Makro implementiert werden. Dies kann ein Problem verursachen.
dirkgently
3
Ein Bind (:: toupper, Konstrukt <unsigned char> (_ 1)) mit boost.lambda wird meiner Meinung nach perfekt funktionieren.
Johannes Schaub - litb
11
Dieser Ansatz funktioniert gut für ASCII, schlägt jedoch für Mehrbyte-Zeichencodierungen oder für spezielle Gehäuseregeln wie das deutsche 'ß' fehl.
dan04
9
Ich habe die akzeptierte Antwort in die Antwort mit den Boost-Bibliotheken geändert, da sie schneller war (in meinen informellen Tests), einfacher zu verwenden war und nicht die mit dieser Lösung verbundenen Probleme aufweist. Immer noch eine gute Lösung für Fälle, in denen Boost nicht verwendet werden kann.
OrangeAlmondSoap
2
Ich kann nicht verstehen, warum der Compiler diesen Code ohne :: qualifier vorher ablehnt toupper. Irgendwelche Ideen?
Sasha.sochka
89

Kurze Lösung mit C ++ 11 und toupper ().

for (auto & c: str) c = toupper(c);
Thanasis Papoutsidakis
quelle
Wäre nicht cvom const charTyp (von auto)? Wenn ja, können Sie es (aufgrund eines constTeils) nicht dem zuweisen , was von zurückgegeben wird toupper(c).
PolGraphic
5
@PolGraphic: Range - based for verwendet die Methoden begin () / end () des Containers, um seinen Inhalt zu durchlaufen. std :: basic_string hat sowohl einen const als auch einen veränderlichen Iterator (zurückgegeben von cbegin () bzw. begin (), siehe std :: basic_string :: begin ), daher verwendet für (:) der entsprechende (cbegin (), wenn str ist deklariert const, mit auto =: = const char, begin () andernfalls mit auto =: = char).
Thanasis Papoutsidakis
5
Siehe dirkgentlys Anser unten, cmuss gegossen werden, unsigned chardamit dies korrigiert wird.
Cris Luengo
Boosts to_upper () scheint viel konsistenter mit c ++ STL-Funktionen zu sein als toupper.
tartaruga_casco_mole
29
struct convert {
   void operator()(char& c) { c = toupper((unsigned char)c); }
};

// ... 
string uc_str;
for_each(uc_str.begin(), uc_str.end(), convert());

Hinweis: Einige Probleme mit der Top-Lösung:

21.5 Nullterminierte Sequenzdienstprogramme

Der Inhalt dieser Header muss mit den Standard-C-Bibliotheksheadern <ctype.h>, <wctype.h>, <string.h>, <wchar.h> und <stdlib.h> [...] identisch sein.

  • Dies bedeutet, dass die cctypeMitglieder 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 charsigniert 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.)

dirkgently
quelle
Die normalen cctype-Mitglieder sind Makros. Ich erinnere mich, dass ich gelesen habe, dass es sich auch um Funktionen handeln musste, obwohl ich keine Kopie des C90-Standards habe und nicht weiß, ob dies ausdrücklich angegeben wurde oder nicht.
David Thornley
1
Sie müssen Funktionen in C ++ sein - auch wenn C erlaubt, dass sie Makros sind. Ich stimme jedoch Ihrem zweiten Punkt bezüglich des Castings zu. Die Top-Lösung könnte negative Werte passieren und damit UB verursachen. Das ist der Grund, warum ich nicht
abgestimmt habe
1
Das Standardzitat darf nicht fehlen: 7.4.2.2/1 (schlechtes Litb, das nur auf einen C99 TC2-Entwurf verweist) und C ++ 17.4.1.2/6 im Glory C ++ 98-Standard.
Johannes Schaub - litb
1
(Beachten Sie die Fußnote dazu: "Dies verbietet die übliche Praxis, ein Maskierungsmakro bereitzustellen ... blah blupp ... die einzige Möglichkeit, dies in C ++ zu tun, besteht darin, eine externe Inline-Funktion bereitzustellen.") :)
Johannes Schaub - Litb
1
... das wird durch diesen Trick erreicht: stackoverflow.com/questions/650461/…
Johannes Schaub - litb
27

Dieses Problem ist mit SIMD für den ASCII-Zeichensatz vektorisierbar .


Beschleunigungsvergleiche:

Vorläufiger Test mit x86-64 gcc 5.2 -O3 -march=nativeauf 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 mit LANG=Cund mit LANG=en_CA.UTF-8.

    Ich habe keinen anderen RangeT als std :: string getestet. Vielleicht ist die andere Formto_upper_copy besser optimiert, aber ich denke , es wird immer new/ mallocPlatz 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 liest std::stringund in a schreibt, char dstbuf[4096]macht zum Testen Sinn.

  • Schleifenaufruf glibc toupper: 6.67s (das intErgebnis wird jedoch nicht auf potenzielle Multi-Byte-UTF-8 überprüft . Dies ist für Türkisch wichtig.)

  • Nur-ASCII-Schleife: 8.79s (meine Basisversion für die folgenden Ergebnisse). Anscheinend ist eine Tabellensuche schneller als eine cmov, wobei die Tabelle in L1 sowieso heiß ist.
  • Nur ASCII-automatisch vektorisiert: 2,51 s . (120 Zeichen liegen auf halbem Weg zwischen dem schlechtesten und dem besten Fall, siehe unten)
  • Nur ASCII manuell vektorisiert: 1,35s

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 -O3aktiviert 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. Das perf record/ reportErgebnis (für das cyclesPerf-Event) ist:

  32.87%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZNK10__cxxabiv121__vmi_class_type_info12__do_dyncastElNS_17__class_type_info10__sub_kindEPKS1_PKvS4_S6_RNS1_16
  21.90%  flipcase-clang-  libstdc++.so.6.0.21   [.] __dynamic_cast                                                                                                 
  16.06%  flipcase-clang-  libc-2.21.so          [.] __GI___strcmp_ssse3                                                                                            
   8.16%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZSt9use_facetISt5ctypeIcEERKT_RKSt6locale                                                                     
   7.84%  flipcase-clang-  flipcase-clang-boost  [.] _Z16strtoupper_boostPcRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE                                   
   2.20%  flipcase-clang-  libstdc++.so.6.0.21   [.] strcmp@plt                                                                                                     
   2.15%  flipcase-clang-  libstdc++.so.6.0.21   [.] __dynamic_cast@plt                                                                                             
   2.14%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZNKSt6locale2id5_M_idEv                                                                                       
   2.11%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZNKSt6locale2id5_M_idEv@plt                                                                                   
   2.08%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZNKSt5ctypeIcE10do_toupperEc                                                                                  
   2.03%  flipcase-clang-  flipcase-clang-boost  [.] _ZSt9use_facetISt5ctypeIcEERKT_RKSt6locale@plt                                                                 
   0.08% ...

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 strlenwerden 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 .

// char, not int, is essential: otherwise gcc unpacks to vectors of int!  Huge slowdown.
char ascii_toupper_char(char c) {
    return ('a' <= c && c <= 'z') ? c^0x20 : c;    // ^ autovectorizes to PXOR: runs on more ports than paddb
}

// gcc can only auto-vectorize loops when the number of iterations is known before the first iteration.  strlen gives us that
size_t strtoupper_autovec(char *dst, const char *src) {
    size_t len = strlen(src);
    for (size_t i=0 ; i<len ; ++i) {
        dst[i] = ascii_toupper_char(src[i]);  // gcc does the vector range check with psubusb / pcmpeqb instead of pcmpgtb
    }
    return len;
}

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.

  • 15 Zeichenfolge: Grundlinie: 1,08 s. autovec: 1,34s
  • 16 Zeichenfolge: Grundlinie: 1,16 s. autovec: 1,52s
  • 127 Zeichenfolge: Grundlinie: 8,91 s. autovec: 2.98s // Nicht-Vektor-Bereinigung muss 15 Zeichen verarbeiten
  • 128 Zeichenfolge: Grundlinie: 9.00s. autovec: 2.06s
  • 129 Zeichenfolge: Grundlinie: 9.04s. autovec: 2.07s // Nicht-Vektor-Bereinigung muss 1 Zeichen verarbeiten

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 <= higheinen einzelnen vorzeichenlosen Vergleich durch Bereichsverschiebung durchführen können, sodass jeder Wert, der kleiner als ist, lowauf einen Wert umbrochen wird, der größer als ist high. (Dies funktioniert, wenn lowund highnicht 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.

#include <immintrin.h>

__m128i upcase_si128(__m128i src) {
    // The above 2 paragraphs were comments here
    __m128i rangeshift = _mm_sub_epi8(src, _mm_set1_epi8('a'+128));
    __m128i nomodify   = _mm_cmpgt_epi8(rangeshift, _mm_set1_epi8(-128 + 25));  // 0:lower case   -1:anything else (upper case or non-alphabetic).  25 = 'z' - 'a'

    __m128i flip  = _mm_andnot_si128(nomodify, _mm_set1_epi8(0x20));            // 0x20:lcase    0:non-lcase

    // just mask the XOR-mask so elements are XORed with 0 instead of 0x20
    return          _mm_xor_si128(src, flip);
    // it's easier to xor with 0x20 or 0 than to AND with ~0x20 or 0xFF
}

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=srcnicht-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 srcauszurichten. Da wir nicht wissen, wo sich die abschließende 0 befindet, wird möglicherweise eine nicht ausgerichtete Last von srcauf 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 .

// FIXME: doesn't always copy the terminating 0.
// microbenchmarks are for this version of the code (with _mm_store in the loop, instead of storeu, for Merom).
size_t strtoupper_sse2(char *dst, const char *src_begin) {
    const char *src = src_begin;
    // scalar until the src pointer is aligned
    while ( (0xf & (uintptr_t)src) && *src ) {
        *(dst++) = ascii_toupper(*(src++));
    }

    if (!*src)
        return src - src_begin;

    // current position (p) is now 16B-aligned, and we're not at the end
    int zero_positions;
    do {
        __m128i sv = _mm_load_si128( (const __m128i*)src );
        // TODO: SSE4.2 PCMPISTRI or PCMPISTRM version to combine the lower-case and '\0' detection?

        __m128i nullcheck = _mm_cmpeq_epi8(_mm_setzero_si128(), sv);
        zero_positions = _mm_movemask_epi8(nullcheck);
        // TODO: unroll so the null-byte check takes less overhead
        if (zero_positions)
            break;

        __m128i upcased = upcase_si128(sv);   // doing this before the loop break lets gcc realize that the constants are still in registers for the unaligned cleanup version.  But it leads to more wasted insns in the early-out case

        _mm_storeu_si128((__m128i*)dst, upcased);
        //_mm_store_si128((__m128i*)dst, upcased);  // for testing on CPUs where storeu is slow
        src += 16;
        dst += 16;
    } while(1);

    // handle the last few bytes.  Options: scalar loop, masked store, or unaligned 16B.
    // rewriting some bytes beyond the end of the string would be easy,
    // but doing a non-atomic read-modify-write outside of the string is not safe.
    // Upcasing is idempotent, so unaligned potentially-overlapping is a good option.

    unsigned int cleanup_bytes = ffs(zero_positions) - 1;  // excluding the trailing null
    const char* last_byte = src + cleanup_bytes;  // points at the terminating '\0'

    // FIXME: copy the terminating 0 when we end at an aligned vector boundary
    // optionally special-case cleanup_bytes == 15: final aligned vector can be used.
    if (cleanup_bytes > 0) {
        if (last_byte - src_begin >= 16) {
            // if src==dest, this load overlaps with the last store:  store-forwarding stall.  Hopefully OOO execution hides it
            __m128i sv = _mm_loadu_si128( (const __m128i*)(last_byte-15) ); // includes the \0
            _mm_storeu_si128((__m128i*)(dst + cleanup_bytes - 15), upcase_si128(sv));
        } else {
            // whole string less than 16B
            // if this is common, try 64b or even 32b cleanup with movq / movd and upcase_si128
#if 1
            for (unsigned int i = 0 ; i <= cleanup_bytes ; ++i) {
                dst[i] = ascii_toupper(src[i]);
            }
#else
            // gcc stupidly auto-vectorizes this, resulting in huge code bloat, but no measurable slowdown because it never runs
            for (int i = cleanup_bytes - 1 ;  i >= 0 ; --i) {
                dst[i] = ascii_toupper(src[i]);
            }
#endif
        }
    }

    return last_byte - src_begin;
}

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.

  • 15 Zeichenfolge: Grundlinie: 1,08 s. autovec: 1,34s. Handbuch: 1.29s
  • 16 Zeichenfolge: Grundlinie: 1,16 s. autovec: 1,52s. Handbuch: 0.335s
  • 31 Zeichenfolge: Handbuch: 0,479s
  • 127 Zeichenfolge: Grundlinie: 8,91 s. autovec: 2.98s. Handbuch: 0,925s
  • 128 Zeichenfolge: Grundlinie: 9.00s. autovec: 2.06s. Handbuch: 0.931s
  • 129 Zeichenfolge: Grundlinie: 9.04s. autovec: 2.07s. Handbuch: 1.02s

(Tatsächlich zeitgesteuert mit _mm_storein 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 movqoder movdLaden / 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 dstPunkt kann um einen anderen Betrag als der srcZeiger vorrücken , aber sobald wir zu einem ausgerichteten srcZeiger zurückkehren, führen wir immer noch nur nicht ausgerichtete Vektorspeicher durch dst.

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 aus toupper('i')ist 'İ'(U0130) nicht 'I'(plain ASCII). Siehe Martin Bonners Kommentare zu einer Frage tolower(), 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 PCMPISTRModer ähnliches möglicherweise viele unserer Überprüfungen auf einmal durchführen.

Peter Cordes
quelle
20

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

Milan Babuškov
quelle
7
Oder das deutsche Eszet (sp?), Das wie der griechische Buchstabe Beta aussieht und "ss" bedeutet. Es gibt kein einzelnes deutsches Zeichen, das "SS" bedeutet, was dem Großbuchstaben entspricht. Das deutsche Wort für "Straße" wird in Großbuchstaben um ein Zeichen länger.
David Thornley
6
Ein weiterer Sonderfall ist der griechische Buchstabe Sigma (Σ), der zwei Kleinbuchstaben enthält, je nachdem, ob er am Ende eines Wortes steht (ς) oder nicht (σ). Und dann gibt es sprachspezifische Regeln, wie zum Beispiel Türkisch mit der Fallzuordnung I↔ı und İ↔i.
Dan04
1
"Uppercasing" wird als Fallfaltung bezeichnet.
Columbo
20
string StringToUpper(string strToConvert)
{
   for (std::string::iterator p = strToConvert.begin(); strToConvert.end() != p; ++p)
       *p = toupper(*p);

   return p;
}

Oder,

string StringToUpper(string strToConvert)
{
    std::transform(strToConvert.begin(), strToConvert.end(), strToConvert.begin(), ::toupper);

    return strToConvert;
}
user648545
quelle
4
Wenn Sie keinen Zugriff auf Boost haben, ist die zweite Lösung wahrscheinlich die beste, die Sie bekommen können. Was machen die Sterne **nach den Parametern der ersten Lösung?
Sam Brinck
1
Ich bin mir ziemlich sicher, dass dies **ein Tippfehler ist, der von dem Versuch übrig geblieben ist, fette Schrift in der Codesyntax zu verwenden.
MasterHD
1
Dieser Code ruft undefiniertes Verhalten auf, wenn toupperer mit negativen Zahlen aufgerufen wird.
Roland Illig
17

Folgendes funktioniert für mich.

#include <algorithm>
void  toUpperCase(std::string& str)
{
    std::transform(str.begin(), str.end(), str.begin(), ::toupper);
}

int main()
{
   std::string str = "hello";
   toUpperCase(&str);
}
Pabitra Dash
quelle
Beachten Sie, dass std :: transform in <algorithm>
edj
Ja. Dieses # Include ist erforderlich, #include <Algorithmus>
Pabitra Dash
1
Dieser Code ruft undefiniertes Verhalten auf, wenn toupperer mit negativen Zahlen aufgerufen wird.
Roland Illig
Duplikat der Antwort von user648545 - -1
Piotr Dobrogost
@PiotrDobrogost Ich habe keine Ahnung von der Antwort von user648545. Ich habe das nicht kopiert. Wenn ich zwei Methoden vergleiche, unterscheidet sich die Methodensignatur insgesamt, obwohl beide Funktionen die Bibliotheksfunktionstransformation verwenden.
Pabitra Dash
13

Verwenden Sie ein Lambda.

std::string s("change my case");

auto to_upper = [] (char_t ch) { return std::use_facet<std::ctype<char_t>>(std::locale()).toupper(ch); };

std::transform(s.begin(), s.end(), s.begin(), to_upper);
Byron
quelle
2
Byron, mach dir keine Sorgen um die anderen Kommentare. Es ist völlig in Ordnung, alte Fragen mit einer neuen (modernen) Lösung zu beantworten, wie Sie es getan haben.
Kyberias
13

Das schnellere, wenn Sie nur ASCII-Zeichen verwenden :

for(i=0;str[i]!=0;i++)
  if(str[i]<='z' && str[i]>='a')
    str[i]-=32;

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.

Luca C.
quelle
1
Die Frage ist mit markiert C++, aber Sie Chaben hier eine Antwort geschrieben. (Ich bin nicht einer der Downvoter.)
hkBattousai
6
Ich habe hier eine C-Antwort UND eine C ++ - Antwort geschrieben, da C ++ so geschrieben ist, dass es vollständig mit C-Quellen kompatibel ist. Daher ist jede C-Lösung auch eine C ++ - korrekte Lösung
Luca C.
Aber es ist viel besser, eine Antwort zu geben, die den C ++ - Weg respektiert.
Dmitriy Yurchenko
Die Standardmethode für C ++ wäre die Verwendung von std :: transform mit toupper. Das ist weniger Code und sicher portabel. Dieser Code basiert auf der "Tatsache", dass das System ASCII als Zeichencodierungsmechanismus verwendet. Nicht sicher, ob alle Systeme auf dieser Codierung basieren, und daher nicht sicher, ob diese portabel ist.
AlexTheo
1
Warum haben Sie sich entschieden, ASCII-Codes anstelle der darin enthaltenen Zeichen zu verwenden '?
HolyBlackCat
11

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:

void strtoupper(char* str)
{ 
    while (*str) *(str++) = toupper((unsigned char)*str);
}

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.

k3a
quelle
Man merkt an, dass diese Antwort eher für einen AC-String als für einen std :: string ist
EvilTeach
Dies hat eine offensichtliche inhärente Sicherheitslücke. Ich würde das nicht tun.
Byron
9
//works for ASCII -- no clear advantage over what is already posted...

std::string toupper(const std::string & s)
{
    std::string ret(s.size(), char());
    for(unsigned int i = 0; i < s.size(); ++i)
        ret[i] = (s[i] <= 'z' && s[i] >= 'a') ? s[i]-('a'-'A') : s[i];
    return ret;
}
David
quelle
s.size () ist vom Typ std :: size_t, wobei
AFAIK
Ich glaube nicht, dass es moderne Implementierungen gibt, in denen das Ergebnis von std :: string :: size signiert ist. Angesichts der Tatsache, dass es sowohl semantisch als auch praktisch keine negative Größe gibt, gehe ich davon aus, dass size_t mindestens eine 32-Bit-Ganzzahl ohne Vorzeichen ist.
user1329482
Es gibt keinen Grund, nicht zu schreiben 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.
Peter Cordes
9
#include <string>
#include <locale>

std::string str = "Hello World!";
auto & f = std::use_facet<std::ctype<char>>(std::locale());
f.toupper(str.data(), str.data() + str.size());

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:

std::use_facet<std::ctype<char> > & f = 
    std::use_facet<std::ctype<char> >(std::locale());
f.toupper(const_cast<char *>(str.data()), str.data() + str.size());
Glen Knowles
quelle
7
typedef std::string::value_type char_t;

char_t up_char( char_t ch )
{
    return std::use_facet< std::ctype< char_t > >( std::locale() ).toupper( ch );
}

std::string toupper( const std::string &src )
{
    std::string result;
    std::transform( src.begin(), src.end(), std::back_inserter( result ), up_char );
    return result;
}

const std::string src  = "test test TEST";

std::cout << toupper( src );
Bayda
quelle
Ich würde keinen back_inserter empfehlen, da Sie die Länge bereits kennen. benutze std :: string result (src.size ()); std :: transform (src.begin (), src.end (), result.begin (), up_char);
Viktor Sehr
Obwohl ich sicher bin, dass Sie das wissen.
Viktor Sehr
@Viktor Sehr, @bayda: Ich weiß, dass dies 2 Jahre alt ist, aber warum nicht das Beste aus beiden Welten bekommen? Verwenden Sie reserveund back_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; }
Evan Teran
4
std::string value;
for (std::string::iterator p = value.begin(); value.end() != p; ++p)
    *p = toupper(*p);
Dmitriy Yurchenko
quelle
Dieser Code ruft undefiniertes Verhalten auf, wenn toupperer mit negativen Zahlen aufgerufen wird.
Roland Illig
2

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 umfasst

zmf
quelle
Dieser Vorschlag ruft undefiniertes Verhalten hervor, wenn toupperer mit negativen Zahlen aufgerufen wird. Du hättest die notwendige Besetzung erwähnen sollen unsigned char.
Roland Illig
2

Hier ist der neueste Code mit C ++ 11

std::string cmd = "Hello World";
for_each(cmd.begin(), cmd.end(), [](char& in){ in = ::toupper(in); });
user2787620
quelle
Dieser Code ruft undefiniertes Verhalten auf, wenn toupperer mit negativen Zahlen aufgerufen wird.
Roland Illig
1

Verwenden von Boost.Text, das für Unicode-Text funktioniert

boost::text::text t = "Hello World";
boost::text::text uppered;
boost::text::to_title(t, std::inserter(uppered, uppered.end()));
std::string newstr = uppered.extract();
cor3ntin
quelle
1

Die Antwort von @dirkgently ist sehr inspirierend, aber ich möchte dies aufgrund der unten gezeigten Besorgnis hervorheben.

Wie bei allen anderen Funktionen von ist das Verhalten von std :: toupper undefiniert, wenn der Wert des Arguments weder als vorzeichenloses Zeichen noch als EOF darstellbar ist. Um diese Funktionen sicher mit einfachen Zeichen (oder vorzeichenbehafteten Zeichen) zu verwenden, sollte das Argument zuerst in vorzeichenloses Zeichen konvertiert werden.
Referenz : std :: toupper

Die korrekte Verwendung von std::touppersollte sein:

#include <algorithm>
#include <cctype>
#include <iostream>
#include <iterator>
#include <string>

void ToUpper(std::string& input)
{
    std::for_each(std::begin(input), std::end(input), [](char& c) {
        c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
    });
}

int main()
{
    std::string s{ "Hello world!" };
    std::cout << s << std::endl;
    ::ToUpper(s);
    std::cout << s << std::endl;

    return 0;
}

Ausgabe:

Hello world!
HELLO WORLD!
KaiserKatze
quelle
0

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.

string StringToUpper(string strToConvert)
{//change each element of the string to upper case
   for(unsigned int i=0;i<strToConvert.length();i++)
   {
      strToConvert[i] = toupper(strToConvert[i]);
   }
   return strToConvert;//return the converted string
}

string StringToLower(string strToConvert)
{//change each element of the string to lower case
   for(unsigned int i=0;i<strToConvert.length();i++)
   {
      strToConvert[i] = tolower(strToConvert[i]);
   }
   return strToConvert;//return the converted string
}
Brandon Stewart
quelle
.length () ist nicht vom Typ 'unsigned int'
malat
Dieser Code ruft undefiniertes Verhalten auf, wenn toupperer mit negativen Zahlen aufgerufen wird.
Roland Illig
0

Meine Lösung (6. Bit für Alpha löschen):

#include <ctype.h>

inline void toupper(char* str)
{
    while (str[i]) {
        if (islower(str[i]))
            str[i] &= ~32; // Clear bit 6 as it is what differs (32) between Upper and Lowercases
        i++;
    }
}
Antonin GAVREL
quelle
Dieser Code ruft undefiniertes Verhalten auf, wenn toupperer mit negativen Zahlen aufgerufen wird.
Roland Illig
Nein ... Bitte überprüfen Sie, ob Sie direkt vor dem Downvoting sind. Islower würde nur mit nicht negativen Werten arbeiten ...
Antonin GAVREL
-1

ALLE diese Lösungen auf dieser Seite sind schwieriger als nötig.

Mach das

RegName = "SomE StRing That you wAnt ConvErTed";
NameLength = RegName.Size();
for (int forLoop = 0; forLoop < NameLength; ++forLoop)
{
     RegName[forLoop] = tolower(RegName[forLoop]);
}

RegNameist dein string. Holen Sie sich Ihre Stringgröße nicht string.size()als eigentlicher Tester verwenden, sehr chaotisch und kann Probleme verursachen. dann. die grundlegendste forSchleife.

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

Geheimnis
quelle
4
Ich sehe nicht, wie dies einfacher ist als die boost :: toupper-Lösung. Können Sie das näher erläutern?
tr9sh
2
Es gibt bereits viele einfache tolowerSchleifen, und die meisten von ihnen verwenden Standardnamen für Schleifenvariablen i, nicht die seltsamen forLoop.
Peter Cordes
-1

Ohne Verwendung von Bibliotheken:

std::string YourClass::Uppercase(const std::string & Text)
{
    std::string UppperCaseString;
    UppperCaseString.reserve(Text.size());
    for (std::string::const_iterator it=Text.begin(); it<Text.end(); ++it)
    {
        UppperCaseString.push_back(((0x60 < *it) && (*it < 0x7B)) ? (*it - static_cast<char>(0x20)) : *it);
    }
    return UppperCaseString;
}
hkBattousai
quelle
Der obige Code funktioniert nur für ASCII-kompatible Codierungen. Weder die Frage noch Ihre Antwort erwähnen diese Einschränkung. Einer von ihnen sollte.
Roland Illig
-1

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.

template<int ...Is>
struct IntVector{
using Type = IntVector<Is...>;
};

template<typename T_Vector, int I_New>
struct PushFront;
template<int ...Is, int I_New>
struct PushFront<IntVector<Is...>,I_New> : IntVector<I_New,Is...>{};

template<int I_Size, typename T_Vector = IntVector<>>
struct Iota : Iota< I_Size-1, typename PushFront<T_Vector,I_Size-1>::Type> {};
template<typename T_Vector>
struct Iota<0,T_Vector> : T_Vector{};

template<char C_In>
struct ToUpperTraits {
    enum { value = (C_In >= 'a' && C_In <='z') ? C_In - ('a'-'A'):C_In };
};

template<typename T>
struct TableToUpper;
template<int ...Is>
struct TableToUpper<IntVector<Is...>>{
    static char at(const char in){
        static const char table[] = {ToUpperTraits<Is>::value...};
        return table[in];
    }
};

int tableToUpper(const char c){
    using Table = TableToUpper<typename Iota<256>::Type>;
    return Table::at(c);
}

mit Anwendungsfall:

std::transform(in.begin(),in.end(),out.begin(),tableToUpper);

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

odinthenerd
quelle
-1
template<size_t size>
char* toupper(char (&dst)[size], const char* src) {
    // generate mapping table once
    static char maptable[256];
    static bool mapped;
    if (!mapped) {
        for (char c = 0; c < 256; c++) {
            if (c >= 'a' && c <= 'z')
                maptable[c] = c & 0xdf;
            else
                maptable[c] = c;
        }
        mapped = true;
    }

    // use mapping table to quickly transform text
    for (int i = 0; *src && i < size; i++) {
        dst[i] = maptable[*(src++)];
    }
    return dst;
}
Ronny Gunawan
quelle
-1

Diese c ++ - Funktion gibt immer die Zeichenfolge in Großbuchstaben zurück ...

#include <locale> 
#include <string>
using namespace std; 
string toUpper (string str){
    locale loc; 
    string n; 
    for (string::size_type i=0; i<str.length(); ++i)
        n += toupper(str[i], loc);
    return n;
}
Awais Jameel
quelle
-3

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.

void to_upper(const std::string str) {
    std::string::iterator it;
    int i;
    for ( i=0;i<str.size();++i ) {
        ((char *)(void *)str.data())[i]=toupper(((char *)str.data())[i]);
    }
}
Erik Aronesty
quelle
I know you're not supposed to modify that data area- Welchen Datenbereich sollen Sie nicht ändern?
user93353
3
Das ist spät, aber was um alles in der Welt? Diese verrückte Linie kann durch str[i] = toupper(str[i]);vollkommen in Ordnung ersetzt werden ( na ja , nicht vollkommen in Ordnung, aber sie behebt die meisten Fehler).
Chris