Gli intervalli alfabetici minuscoli e maiuscoli non attraversano un %32limite di "allineamento" nel sistema di codifica ASCII.
Questo è il motivo per cui bit 0x20è l'unica differenza tra le versioni maiuscole / minuscole della stessa lettera.
Se così non fosse, avresti bisogno di aggiungere o sottrarre 0x20, non solo attivare e disattivare, e per alcune lettere verrebbe eseguito per capovolgere altri bit più alti. (E non ci sarebbe una singola operazione che potrebbe alternare, e in primo luogo verificare la presenza di caratteri alfabetici sarebbe più difficile perché non potresti | = 0x20 per forzare lcase.)
Trucchi solo ASCII correlati: è possibile verificare la presenza di un carattere ASCII alfabetico forzando le lettere minuscole con c |= 0x20e quindi se (non firmato) c - 'a' <= ('z'-'a'). Quindi solo 3 operazioni: OR + SUB + CMP contro un costante 25. Naturalmente, i compilatori sanno come ottimizzare (c>='a' && c<='z') in asm come questo per te , quindi al massimo dovresti fare la c|=0x20parte da solo. È piuttosto scomodo fare tutto il cast necessario, in particolare per aggirare le promozioni di numeri interi predefiniti da firmare int.
unsigned char lcase = y|0x20;
if (lcase - 'a' <= (unsigned)('z'-'a')) { // lcase-'a' will wrap for characters below 'a'
// c is alphabetic ASCII
}
// else it's not
Vedi anche Converti una stringa in C ++ in maiuscolo (stringa SIMD touppersolo per ASCII, mascherando l'operando per XOR usando quel controllo).
E anche Come accedere a un array di caratteri e cambiare le lettere minuscole in lettere maiuscole e viceversa
(C con intrinseche SIMD e scalare x86 asm maiuscole / minuscole per caratteri ASCII alfabetici, lasciando gli altri non modificati).
Questi trucchi sono per lo più utili solo se si ottimizza a mano alcune elaborazioni di testo con SIMD (es. SSE2 o NEON), dopo aver verificato che nessuna delle chars in un vettore abbia il bit alto impostato. (E quindi nessuno dei byte fa parte di una codifica UTF-8 multi-byte per un singolo carattere, che potrebbe avere diverse maiuscole / minuscole). Se ne trovi, puoi tornare allo scalare per questo blocco di 16 byte o per il resto della stringa.
Ci sono anche alcuni locali in cui toupper()o tolower()su alcuni caratteri nell'intervallo ASCII producono caratteri al di fuori di tale intervallo, in particolare il turco dove I ↔ ı e İ ↔ i. In quei locali, avresti bisogno di un controllo più sofisticato, o probabilmente non tenterai affatto di utilizzare questa ottimizzazione.
Ma in alcuni casi, ti è consentito assumere ASCII invece di UTF-8, ad esempio utilità Unix con LANG=C(la locale POSIX), non en_CA.UTF-8o altro.
Ma se riesci a verificare che è sicuro, puoi toupperstringhe di media lunghezza molto più velocemente rispetto a chiamare toupper()in un ciclo (come 5x), e alla fine ho provato con Boost 1.58 , molto più veloce di quello boost::to_upper_copy<char*, std::string>()che fa uno stupido dynamic_castper ogni personaggio.
@in `usando^ 32.