Converti una stringa in C ++ in maiuscolo


268

Come si può convertire una stringa in maiuscolo. Gli esempi che ho trovato su Google hanno a che fare solo con i caratteri.

Risposte:


205

Potenzia gli algoritmi delle stringhe:

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

5
Questo ha anche il vantaggio di i18n, dove ::touppermolto probabilmente assume l'ASCII.
Ben Straub,

4
L'ultima riga non viene compilata - devi passare a qualcosa del tipo:std::string newstr(boost::to_upper_copy<std::string>("Hello World"));
maxschlepzig

58
questa non dovrebbe essere la risposta accettata poiché richiede un potenziamento o il titolo dovrebbe essere cambiato.
Andrea,

44
si, installerò boost solo per to_upper ... ottima idea! </ sarcasmo> :)
thang

12
Personalmente sono mal disposto a boost come risposta a "come faccio x in C ++?" perché boost non è affatto una soluzione leggera. Sembra che tu acquisti in boost come framework (o ACE o Qt o Recusion ToolKit ++ o ...) oppure no. Preferirei vedere soluzioni linguistiche.
jwm,

486
#include <algorithm>
#include <string>

std::string str = "Hello World";
std::transform(str.begin(), str.end(),str.begin(), ::toupper);

8
In realtà, toupper()può essere implementato come una macro. Ciò può causare un problema.
diretto il

3
a bind (:: toupper, construct <unsigned char> (_ 1)) con boost.lambda funzionerà perfettamente, penso.
Johannes Schaub - litb

11
Questo approccio funziona bene per ASCII, ma non riesce per le codifiche di caratteri multi-byte o per regole di casing speciali come il tedesco "ß".
dan04

9
Ho cambiato la risposta accettata a quella usando le librerie boost, perché era più veloce (nei miei test informali), più facile da usare e non ha i problemi associati a questa soluzione. Ancora una buona soluzione per i casi in cui non è possibile utilizzare boost.
OrangeAlmondSoap

2
Non riesco a capire perché il compilatore rifiuta questo codice senza :: qualificatore prima toupper. Qualche idea?
sasha.sochka,

90

Breve soluzione usando C ++ 11 e toupper ().

for (auto & c: str) c = toupper(c);

Non csarebbe di const chartipo (da auto)? In tal caso, non è possibile assegnarlo (a causa della constparte) a ciò che viene restituito toupper(c).
PolGraphic,

5
@PolGraphic: Range - based per usa i metodi begin () / end () del container per scorrere i suoi contenuti. std :: basic_string ha sia un const che un mutabile iteratore (restituiti rispettivamente da cbegin () e begin (), vedi std :: basic_string :: begin ), quindi per (:) usa quello appropriato (cbegin () se str è const dichiarata, con auto =: = const char, begin () altrimenti, con auto =: = char).
Thanasis Papoutsidakis,

5
Vedi la risposta di dirkgently di seguito, è cnecessario eseguire il cast unsigned charper correggere questo.
Cris Luengo,

to_upper () di boost sembra molto più coerente con le funzioni c ++ STL di 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());

Nota: un paio di problemi con la soluzione migliore:

21.5 Utilità di sequenza senza terminazione

Il contenuto di queste intestazioni deve essere uguale alle intestazioni della libreria C standard <ctype.h>, <wctype.h>, <string.h>, <wchar.h> e <stdlib.h> [...]

  • Ciò significa che i cctypemembri potrebbero essere macro non adatte al consumo diretto negli algoritmi standard.

  • Un altro problema con lo stesso esempio è che non lancia l'argomento né verifica che ciò non sia negativo; questo è particolarmente pericoloso per i sistemi in cui charè firmato plain . (Il motivo è: se questo è implementato come una macro, probabilmente utilizzerà una tabella di ricerca e il tuo argomento si indicherà in quella tabella. Un indice negativo ti darà UB.)


I normali membri cctype sono macro. Ricordo di aver letto che dovevano essere anche funzioni, anche se non ho una copia dello standard C90 e non so se sia stato esplicitamente dichiarato o meno.
David Thornley,

1
devono essere funzioni in C ++, anche se C consente loro di essere macro. sono d'accordo con il tuo secondo punto sul casting però. la soluzione migliore potrebbe passare valori negativi e causare UB con quello. questo è il motivo per cui non l'ho votato (ma non l'ho nemmeno votato) :)
Johannes Schaub - lett

1
la citazione standard non deve mancare: 7.4.2.2/1 (povero litb, che fa riferimento solo a una bozza C99 TC2) e C ++ 17.4.1.2/6 nella gloria standard c ++ 98.
Johannes Schaub - litb

1
(nota la nota a piè di pagina: "Questo non consente la pratica comune di fornire una macro di mascheramento .... blah blupp .. l'unico modo per farlo in C ++ è fornire una funzione inline esterna.") :)
Johannes Schaub -


27

Questo problema è realizzabile con SIMD per il set di caratteri ASCII.


Confronti di accelerazione:

Test preliminari con x86-64 gcc 5.2 -O3 -march=nativesu un Core2Duo (Merom). La stessa stringa di 120 caratteri (ASCII minuscole e non minuscole miste), convertita in un loop 40M volte (senza allineamento tra file, quindi il compilatore non può ottimizzare via o sollevare nulla dal loop). Stessi buffer di origine e dest, quindi nessun sovraccarico malloc o effetti memoria / cache: i dati sono caldi nella cache L1 per tutto il tempo e siamo puramente legati alla CPU.

  • boost::to_upper_copy<char*, std::string>(): 198.0s . Sì, Boost 1.58 su Ubuntu 15.10 è davvero così lento. Ho profilato e fatto un singolo passo in avanti in un debugger, ed è davvero pessimo: c'è un Dynamic_cast di una variabile locale in corso per personaggio !!! (dynamic_cast accetta più chiamate su strcmp). Questo succede con LANG=Ce con LANG=en_CA.UTF-8.

    Non ho testato usando un RangeT diverso da std :: string. Forse l'altra forma dito_upper_copy ottimizza meglio, ma penso che sarà sempre new/ mallocspazio per la copia, quindi è più difficile testarlo. Forse qualcosa che ho fatto differisce da un normale caso d'uso, e forse normalmente fermato g ++ può sollevare le cose di configurazione della localizzazione dal ciclo per carattere. Il mio ciclo di lettura da std::stringe scrittura a char dstbuf[4096]ha senso per i test.

  • loop che chiama glibc toupper: 6.67s (non controllando il intrisultato per il potenziale UTF-8 multi-byte, questo è importante per il turco).

  • Loop solo ASCII: 8.79s (la mia versione di base per i risultati di seguito.) Apparentemente una ricerca della tabella è più veloce di una cmov, con la tabella calda in L1 comunque.
  • Vettorializzazione automatica solo ASCII: 2,51 s . (120 caratteri è a metà strada tra il caso peggiore e il caso migliore, vedi sotto)
  • Solo ASCII vettorializzato manualmente: 1,35 s

Vedi anche questa domanda toupper()sull'essere lento su Windows quando è impostata un'impostazione internazionale .


Sono rimasto scioccato dal fatto che Boost sia un ordine di grandezza più lento delle altre opzioni. Ho ricontrollato che avevo -O3abilitato e ho anche fatto un singolo passo in avanti per vedere cosa stava facendo. È quasi esattamente la stessa velocità con clang ++ 3.8. Ha un enorme sovraccarico all'interno del ciclo per personaggio. Il risultato perf record/ report(per l' cyclesevento perf) è:

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

autovettorizazione

Gcc e clang vettorializzano automaticamente i loop solo quando il conteggio delle iterazioni è noto prima del loop. (ad esempio, i cicli di ricerca come l'implementazione in chiaro di C strlennon si autovectorizza.)

Pertanto, per le stringhe abbastanza piccole da adattarsi alla cache, otteniamo una notevole velocità per stringhe ~ 128 caratteri lunghi rispetto alla strlenprima. Ciò non sarà necessario per le stringhe di lunghezza esplicita (come C ++ std::string).

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

Qualsiasi libc decente avrà un efficiente strlenche è molto più veloce del looping di un byte alla volta, quindi i circuiti separati e gli anelli di toupper separati sono più veloci.

Linea di base: un ciclo che verifica al volo uno 0 finale.

Tempi per 40 M di iterazioni, su un Core2 (Merom) a 2,4 GHz. gcc 5.2 -O3 -march=native. (Ubuntu 15.10). dst != src(quindi ne facciamo una copia), ma non si sovrappongono (e non sono nelle vicinanze). Entrambi sono allineati.

  • 15 stringhe di caratteri: linea di base: 1,08 s. autovec: 1.34s
  • 16 stringhe di caratteri: linea di base: 1,16 s. autovec: 1.52s
  • 127 stringa di caratteri: linea di base: 8.91s. autovec: 2.98s // la pulizia non vettoriale ha 15 caratteri da elaborare
  • 128 char string: baseline: 9.00s. autovec: 2.06s
  • 129 stringa di caratteri: baseline: 9.04s. autovec: 2.07s // la pulizia non vettoriale ha 1 carattere da elaborare

Alcuni risultati sono leggermente diversi con clang.

Il loop microbenchmark che chiama la funzione si trova in un file separato. Altrimenti si allinea e strlen()viene sollevato fuori dal giro, e corre drasticamente più veloce, esp. per 16 stringhe di caratteri (0,187 s).

Questo ha il grande vantaggio che gcc può auto-vettorializzarlo per qualsiasi architettura, ma il principale svantaggio è che è più lento nel caso comune di stringhe di piccole dimensioni.


Quindi ci sono grandi accelerazioni, ma l'auto-vettorializzazione del compilatore non è un ottimo codice, esp. per la pulizia degli ultimi fino a 15 caratteri.

Vettorializzazione manuale con intrinseci SSE:

Basato sulla mia funzione di capovolgimento che inverte il caso di ogni carattere alfabetico. Sfrutta il "trucco comparativo senza segno", in cui è possibile eseguire low < a && a <= highun singolo confronto senza segno mediante lo spostamento di intervallo, in modo che qualsiasi valore inferiore a quello si lowsovrapponga a un valore maggiore di high. (Funziona se lowe highnon sono troppo distanti.)

SSE ha solo un confronto con segno maggiore, ma possiamo ancora usare il trucco "confronto senza segno" spostando il range alla fine dell'intervallo con segno: Sottrai 'a' + 128, quindi i caratteri alfabetici vanno da -128 a -128 +25 (-128 + 'z' - 'a')

Si noti che l'aggiunta di 128 e la sottrazione di 128 sono la stessa cosa per numeri interi a 8 bit. Non c'è nessun posto dove andare per il carry, quindi è solo xor (carryless add), lanciando il bit alto.

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

Data questa funzione che funziona per un vettore, possiamo chiamarlo in un ciclo per elaborare un'intera stringa. Dato che stiamo già prendendo di mira SSE2, possiamo eseguire contemporaneamente un controllo vettoriale di fine stringa.

Possiamo anche fare molto meglio per la "pulizia" degli ultimi fino a 15 byte rimasti dopo aver eseguito vettori di 16B: il maiuscolo è idempotente, quindi rielaborare alcuni byte di input va bene. Eseguiamo un carico non allineato degli ultimi 16 B del sorgente e lo memorizziamo nel buffer dest sovrapponendo l'ultimo archivio 16B dal ciclo.

L'unica volta che non funziona è quando l'intera stringa è sotto 16B: Anche quando dst=src, la lettura-modifica-scrittura non atomica non è la stessa cosa di non toccare affatto alcuni byte e può rompere il codice multithread.

Abbiamo un loop scalare per questo, e anche per srcallinearci. Dato che non sappiamo dove si troverà lo 0 che termina, un carico non allineato srcpotrebbe attraversare la pagina successiva e segfault. Se abbiamo bisogno di byte in un blocco 16B allineato, è sempre sicuro caricare l'intero blocco 16B allineato.

Fonte completa: in sintesi .

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

Tempi per 40 M di iterazioni, su un Core2 (Merom) a 2,4 GHz. gcc 5.2 -O3 -march=native. (Ubuntu 15.10). dst != src(quindi ne facciamo una copia), ma non si sovrappongono (e non sono nelle vicinanze). Entrambi sono allineati.

  • 15 stringhe di caratteri: linea di base: 1,08 s. autovec: 1.34s. manuale: 1,29 s
  • 16 stringhe di caratteri: linea di base: 1,16 s. autovec: 1.52s. manuale: 0.335s
  • 31 stringhe di caratteri: manuale: 0.479s
  • 127 stringa di caratteri: baseline: 8.91s. autovec: 2.98s. manuale: 0.925s
  • 128 char string: baseline: 9.00s. autovec: 2.06s. manuale: 0.931s
  • 129 stringa di caratteri: baseline: 9.04s. autovec: 2.07s. manuale: 1.02s

(In realtà è sincronizzato con _mm_storenel ciclo, no _mm_storeu, perché storeu è più lento su Merom anche quando l'indirizzo è allineato. Va bene su Nehalem e versioni successive. Ho anche lasciato il codice così com'è per ora, invece di correggere l'errore di copia lo 0 che termina in alcuni casi, perché non voglio riprogrammare tutto.)

Quindi per stringhe corte più lunghe di 16B, questo è notevolmente più veloce di auto-vettorializzare. Le lunghezze una larghezza inferiore a un vettore non presentano problemi. Potrebbero essere un problema quando si opera sul posto, a causa di uno stallo di inoltro del negozio. (Ma nota che è ancora bene elaborare il nostro output, piuttosto che l'input originale, perché toupper è idempotente).

C'è molto spazio per ottimizzare questo per diversi casi d'uso, a seconda di ciò che vuole il codice circostante e della microarchitettura di destinazione. Far in modo che il compilatore emetta un bel codice per la parte di pulizia è complicato. L'uso ffs(3)(che si compila in bsf o tzcnt su x86) sembra essere buono, ma ovviamente quel bit ha bisogno di un ripensamento poiché ho notato un bug dopo aver scritto la maggior parte di questa risposta (vedere i commenti FIXME).

Accelerazioni vettoriali per stringhe ancora più piccole possono essere ottenute con movqo movdcarichi / negozi. Personalizza secondo necessità per il tuo caso d'uso.


UTF-8:

Siamo in grado di rilevare quando il nostro vettore ha dei byte con il bit alto impostato e, in tal caso, ricorrere a un loop scalare compatibile con utf-8 per quel vettore. Il dstpunto può avanzare di una quantità diversa rispetto al srcpuntatore, ma una volta tornato a un srcpuntatore allineato , eseguiremo comunque solo negozi vettoriali non allineati dst.

Per il testo UTF-8, ma principalmente costituito dal sottoinsieme ASCII di UTF-8, questo può essere buono: prestazioni elevate nel caso comune con comportamento corretto in tutti i casi. Quando c'è un sacco di non-ASCII, probabilmente sarà comunque peggio che rimanere sempre nel loop consapevole scalare UTF-8.

Rendere l'inglese più veloce a spese di altre lingue non è una decisione a prova di futuro se il rovescio della medaglia è significativo.


Locale-aware:

Nella locale turca ( tr_TR), il risultato corretto da toupper('i')è 'İ'(U0130), non 'I'(semplice ASCII). Vedi i commenti di Martin Bonner su una domanda tolower()sull'essere lento su Windows.

Possiamo anche verificare un elenco di eccezioni e il fallback su scalare lì, come per i caratteri di input UTF8 multi-byte.

Con questa complessità, SSE4.2 PCMPISTRMo qualcosa del genere potrebbe essere in grado di eseguire molti dei nostri controlli in una volta sola.


20

Hai caratteri ASCII o internazionali nelle stringhe?

Se si tratta dell'ultimo caso, "maiuscole" non è così semplice e dipende dall'alfabeto utilizzato. Ci sono alfabeti bicamerali e unicamerali. Solo gli alfabeti bicamerali hanno caratteri diversi per le lettere maiuscole e minuscole. Inoltre, ci sono caratteri compositi, come la lettera maiuscola latina 'DZ' (\ u01F1 'DZ') che usano il cosiddetto caso del titolo . Ciò significa che viene modificato solo il primo carattere (D).

Ti suggerisco di esaminare l' ICU e la differenza tra mappature di casi semplici e complete. Questo potrebbe aiutare:

http://userguide.icu-project.org/transforms/casemappings


7
O il tedesco eszet (sp?), La cosa che sembra la lettera greca beta, e significa "ss". Non esiste un singolo carattere tedesco che significhi "SS", che è l'equivalente maiuscolo. La parola tedesca per "strada", se maiuscola, ottiene un carattere in più.
David Thornley,

6
Un altro caso speciale è la lettera greca sigma (Σ), che ha due versioni minuscole, a seconda che si trovi alla fine di una parola (ς) o meno (σ). E poi ci sono regole specifiche per la lingua, come il turco con la mappatura dei casi I↔ı e ↔↔i.
dan04

1
"Uppercasing" si chiama custodia pieghevole.
Columbo,

20
string StringToUpper(string strToConvert)
{
   for (std::string::iterator p = strToConvert.begin(); strToConvert.end() != p; ++p)
       *p = toupper(*p);

   return p;
}

O,

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

    return strToConvert;
}

4
se non hai accesso per potenziare la seconda soluzione è probabilmente la migliore che puoi ottenere. cosa fanno le stelle **dopo i parametri sulla prima soluzione?
Sam Brinck il

1
Sono abbastanza sicuro che **sia rimasto un errore di battitura nel tentativo di usare il carattere grassetto nella sintassi del codice.
MasterHD,

1
Questo codice richiama comportamenti indefiniti quando toupperviene chiamato con numeri negativi.
Roland Illig,

17

Quanto segue funziona per me.

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

int main()
{
   std::string str = "hello";
   toUpperCase(&str);
}

Si noti che std :: transform è definito in <algorithm>
edj

Sì. questo # include è richiesto, #include <algorithm>
Pabitra Dash

1
Questo codice richiama comportamenti indefiniti quando toupperviene chiamato con numeri negativi.
Roland Illig,

duplicato della risposta data dall'utente648545 - -1
Piotr Dobrogost

@PiotrDobrogost Non ho idea della risposta data dall'utente648545. Non l'ho copiato. Quando confronto due metodi, la firma del metodo è completamente diversa, sebbene entrambe le funzioni utilizzino la trasformazione della funzione di libreria.
Pabitra Dash,

13

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

2
Byron, non preoccuparti degli altri commenti. Va bene rispondere a vecchie domande con una nuova soluzione (moderna) come hai fatto tu.
Kyberias,

13

Quello più veloce se usi solo caratteri ASCII :

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

Si noti che questo codice viene eseguito più velocemente ma funziona solo su ASCII e non è una soluzione "astratta".

Se hai bisogno di soluzioni UNICODE o di soluzioni più convenzionali e astratte, cerca altre risposte e lavora con i metodi delle stringhe C ++.


1
La domanda è contrassegnata come C++, ma hai scritto una Crisposta qui. (Non sono uno dei downvoter.)
hkBattousai,

6
Ho scritto una risposta C E una risposta C ++ qui perché C ++ è scritto per essere pienamente compatibile con le fonti C, quindi qualsiasi soluzione C è anche una soluzione corretta C ++
Luca

Ma è molto meglio dare una risposta che rispetti il ​​modo C ++.
Dmitriy Yurchenko il

Il modo c ++ standard sarebbe usare std :: transform con toupper. Questo è meno codice e sicuramente portatile. Questo codice si basa sul "fatto" che il sistema utilizzerà ASCII come meccanismo di codifica dei caratteri. Non sono sicuro che tutti i sistemi siano basati su questa codifica e quindi non sono sicuri che sia portatile.
AlexTheo,

1
Perché hai deciso di utilizzare i codici ASCII anziché i caratteri racchiusi in '?
HolyBlackCat

11

Finché stai bene solo con ASCII e puoi fornire un puntatore valido alla memoria RW, c'è un one-liner semplice e molto efficace in C:

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

Ciò è particolarmente utile per stringhe semplici come identificatori ASCII che si desidera normalizzare nello stesso caso di caratteri. È quindi possibile utilizzare il buffer per costruire un'istanza std: string.


Si nota che questa risposta è per stringa ac anziché per std :: string
EvilTeach

Ciò ha un evidente difetto di sicurezza intrinseco. Non lo farei.
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;
}

s.size () è di tipo std :: size_t che, AFAIK potrebbe benissimo essere unsigned int a seconda dell'implementazione
odinthenerd

Non penso che ci siano implementazioni moderne in cui sia firmato il risultato di std :: string :: size. Dato che, sia semanticamente che praticamente, non esiste una dimensione negativa, vado con size_t che è almeno un intero senza segno a 32 bit.
user1329482

Non c'è motivo di non scrivere for (size_t i = 0 .... Inoltre, non c'è alcun buon motivo per renderlo così difficile da leggere. Questo prima copia anche la stringa e poi ci passa sopra. @La risposta di Luke è migliore in qualche modo, tranne che per non sfruttare le 'a'costanti di carattere.
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());

Questo funzionerà meglio di tutte le risposte che usano la funzione toupper globale, ed è presumibilmente ciò che sta facendo boost :: to_upper.

Questo perché :: toupper deve cercare la locale - perché potrebbe essere stata cambiata da un thread diverso - per ogni invocazione, mentre qui solo la chiamata a locale () ha questa penalità. E cercare le impostazioni locali in genere comporta il blocco.

Questo funziona anche con C ++ 98 dopo aver sostituito l'auto, l'uso del nuovo str.data () non costante e l'aggiunta di uno spazio per interrompere la chiusura del modello (">>" in ">>") in questo modo:

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

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

non consiglierei un back_inserter perché conosci già la lunghezza; usa std :: string result (src.size ()); std :: transform (src.begin (), src.end (), result.begin (), up_char);
Viktor Sehr,

Anche se sono sicuro che tu lo sappia.
Viktor Sehr,

@Viktor Sehr, @bayda: so che ha 2 anni, ma perché non ottenere il meglio da entrambi i mondi. Usa reservee back_inserter(facendo in modo che la stringa venga copiata una sola volta). 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);

Questo codice richiama comportamenti indefiniti quando toupperviene chiamato con numeri negativi.
Roland Illig,

2

prova la toupper()funzione ( #include <ctype.h>). accetta caratteri come argomenti, le stringhe sono costituite da caratteri, quindi dovrai iterare su ogni singolo carattere che quando messo insieme comprende la stringa


Questo suggerimento richiama comportamenti indefiniti quando toupperviene chiamato con numeri negativi. Avresti dovuto menzionare il cast necessario unsigned char.
Roland Illig,

2

Ecco l'ultimo codice con C ++ 11

std::string cmd = "Hello World";
for_each(cmd.begin(), cmd.end(), [](char& in){ in = ::toupper(in); });

Questo codice richiama comportamenti indefiniti quando toupperviene chiamato con numeri negativi.
Roland Illig,

1

Utilizzando Boost.Text, che funzionerà per il testo Unicode

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

1

La risposta di @dirkgently è molto stimolante, ma voglio sottolineare che a causa della preoccupazione come mostrato di seguito,

Come tutte le altre funzioni di, il comportamento di std :: toupper non è definito se il valore dell'argomento non è né rappresentabile come carattere senza segno né uguale a EOF. Per utilizzare queste funzioni in modo sicuro con caratteri semplici (o caratteri firmati), l'argomento deve essere prima convertito in caratteri non firmati
Riferimento : std :: toupper

l'uso corretto di std::toupperdovrebbe essere:

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

Produzione:

Hello world!
HELLO WORLD!

0

non sono sicuro che ci sia una funzione integrata. Prova questo:

Includere le librerie ctype.h OR cctype e stdlib.h come parte delle direttive del preprocessore.

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
}

.length () non è di tipo 'unsigned int'
malat

Questo codice richiama comportamenti indefiniti quando toupperviene chiamato con numeri negativi.
Roland Illig,

0

La mia soluzione (cancellando il sesto bit per l'alfa):

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

Questo codice richiama comportamenti indefiniti quando toupperviene chiamato con numeri negativi.
Roland Illig,

No ... Verifica di avere ragione prima di effettuare il downgrade. Islower avrebbe funzionato solo su valori non negativi ...
Antonin GAVREL

-1

TUTTE queste soluzioni in questa pagina sono più difficili di quanto debbano essere.

Fai questo

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

RegNameè tuo string. Ottieni le dimensioni della tua stringa non utilizzare string.size()come tester reale, molto disordinato e può causare problemi. poi. il forciclo più elementare .

ricorda che la dimensione della stringa restituisce anche il delimitatore, quindi usa <e non <= nel tuo loop test.

l'output sarà: una stringa che vuoi convertire


4
Non vedo come sia più semplice della soluzione boost :: toupper. Puoi elaborare?
tr9sh

2
Esistono già molti semplici tolowerloop e la maggior parte utilizza nomi di variabili di loop standard come i, non i strani forLoop.
Peter Cordes,

-1

Senza utilizzare alcuna libreria:

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

Il codice sopra funziona solo per codifiche compatibili ASCII. Né la domanda che non la tua risposta menziona questa limitazione. Uno di loro dovrebbe.
Roland Illig,

-1

Se ti occupi solo di caratteri a 8 bit (che assumono anche tutte le altre risposte tranne Milan Babuškov) puoi ottenere la velocità più veloce generando una tabella di ricerca al momento della compilazione usando la metaprogrammazione. Su ideone.com funziona 7 volte più veloce della funzione libreria e 3 volte più veloce di una versione scritta a mano ( http://ideone.com/sb1Rup ). È anche personalizzabile attraverso tratti senza rallentamento.

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

con caso d'uso:

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

Per una descrizione approfondita (molte pagine) di come funziona mi permetto di collegare spudoratamente il mio blog: http://metaporky.blogspot.de/2014/07/part-4-generating-look-up-tables-at.html


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

-1

Questa funzione c ++ restituisce sempre la stringa maiuscola ...

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

-3

Uso questa soluzione. So che non dovresti modificare quell'area dati ... ma penso che sia principalmente per i bug di sovraccarico del buffer e il carattere null ... le cose del case superiore non sono le stesse.

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

I know you're not supposed to modify that data area- quale area dati non dovresti modificare?
user93353

3
È tardi, ma che diamine? Quella linea folle può essere sostituita con str[i] = toupper(str[i]);perfettamente bene (beh, non perfettamente bene, ma risolve la maggior parte delle cose sbagliate).
chris,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.