Cum se putea converti un șir de caractere cu majuscule. Exemple am aflat de pe google doar au de a face cu caractere.
struct convert {
void operator()(char& c) { c = toupper((unsigned char)c); }
};
// ...
string uc_str;
for_each(uc_str.begin(), uc_str.end(), convert());
Notă: Un cuplu de probleme cu soluție de top:
21.5 Null-terminate secvență de utilități
conținutul de aceste antete sunt aceleași ca și în C Standard Library cap <ctype.h>, <wctype.h>, <string.h>, <wchar.h> și <stdlib.h> [...]
Ceea ce înseamnă că cctype
membrii pot fi de macro-uri nu sunt potrivite pentru consumul direct în algoritmi standard.
O altă problemă cu același exemplu este faptul că nu a aruncat argumentul sau să verifice dacă acest lucru este non-negative; acest lucru este deosebit de periculos pentru sistemele de unde plain char
este semnat. (Motivul fiind: dacă acesta este implementat ca un macro, probabil, se va utiliza un tabel de căutare și argumentul tău indici în masă. Un indice negativ va da UB.)
Testarea preliminară cu x86-64 gcc 5.2 `-O3 -martie=nativ pe un Core2Duo (Merom). Același șir de 120 caractere (amestecat cu litere mici și non-mici ASCII), transformată într-o buclă de 40 de milioane de ori (cu nici o cruce-fișier inlining, astfel încât compilatorul poate't optimiza departe sau ridicare de orice iasă din buclă). Aceeași sursă și dest tampoane, deci nu malloc aeriene sau memorie/cache efecte: datele fierbinte în L1 cache tot timpul, și ne-am're pur CPU-bound.
boost::to_upper_copy<char*, std::string>()
: 198.0 s. Da, pentru a Stimula 1.58 pe Ubuntu 15.10 este foarte lentă. Am dedus singur și-a călcat așm într-un debugger, și-l's într-adevăr, într-adevăr ** rău:'s o dynamic_cast de o localizare variabilă întâmplă pe caracter!!! (dynamic_cast are mai multe apeluri pentru a strcmp). Acest lucru se întâmplă cu LANG=C
și cu LANG=en_CA.UTF-8
.
Am't de testare folosind un RangeT altele decât std::string. Poate altă formă de to_upper_copy
optimizeaza mai bine, dar cred ca va new
/malloc
spațiu pentru a copia, deci's greu de testat. Poate am facut ceva diferă de la o utilizare normală-un caz, și poate, în mod normal, oprit g++ poate ridica locale setup lucrurile din per-caracter curent. Bucla mea de lectură de la un std::stringși scrierea unei
char dstbuf[4096]` logic pentru testare. toupper
: 6.67 s (nu am verificat int
rezultat pentru potențialii multi-byte UTF-8, deși. Aceasta contează pentru turci.) Am fost șocat că Boost este un ordin de mărime mai lent decât alte opțiuni. Am verificat asta am avut -O3
activat, și chiar și un singur călcat așm pentru a vedea ce face. L's aproape exact aceeași viteză cu zăngănit++ 3.8. Are mare deasupra capului în interiorul per-caracter curent. Anii perf record
/ "raport" rezultatul (pentru ciclurile` perf eveniment) este:
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% ...
Ccg și zăngănit va auto-vectorizare bucle atunci când repetare conta este cunoscut înainte de bucla. (de exemplu, căutarea bucle ca simplă-implementare C a strlen va't autovectorize.) Astfel, pentru siruri de caractere suficient de mic pentru a încăpea în memoria cache, vom obține o accelerare semnificativă pentru siruri de caractere ~128 de caractere lung de la a face
strlen prima. Asta nu't fi necesare pentru explicite-lungime siruri de caractere (cum ar fi 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;
}
Orice decent libc va avea un sistem eficient strlen
care's mult mai repede decât looping un octet la un moment dat, atât de separate vectorized strlen și toupper buclele sunt mai rapide.
De referință: o buclă care verifică pentru o încheiere 0 pe zbor.
Ori pentru 40M iterații, pe un Core2 (Merom) de 2.4 GHz. gcc 5.2 -O3 -martie=nativ
. (Ubuntu 15.10). dst != src
(deci, vom face o copie), dar acestea nu't se suprapun (și nu't apropiere). Ambele sunt aliniate.
strlen()
devine arborat pe dinafară, și se execută în mod dramatic mai rapid, esp. pentru 16 char siruri de caractere (0.187 s).
Acest lucru are avantajul major că ccg poate auto-vectorizare pentru orice arhitectură, dar dezavantajul major pe care-l's mai lent, de obicei-caz obișnuit de mic siruri de caractere. Deci nu sunt mari speedups, dar compiler auto-vectorizare nu't face mare cod, esp. pentru curatare de ultima până la 15 caractere.
Bazat pe caz flip funcția care inversează caz de orice caracter alfabetic. Este nevoie de profite de "nesemnat compara truc", în cazul în care puteți face `low < && a <= mare cu un singur nesemnate comparație cu intervalul de schimbare, astfel încât orice valoare mai mică decât "scăzut" împachetări la o valoare's mai mari decât "ridicat". (Aceasta funcționează dacă "scăzut" și "ridicat" n't prea departe unul de altul.) SSE doar a semnat compara-o mai mare, dar încă mai putem folosi "nesemnat compara" trucul de gamă-deplasarea în jos a semnat gama: Scad 'un'+128, astfel încât caractere alfabetice variază de la -128 la -128+25 (-128+'z'-'un') Rețineți că adăugarea de 128 și scăderea 128 sunt același lucru pentru 8bit numere întregi. Nu's nicăieri pentru a transporta pentru a merge, așa că's doar xor (carryless adăuga), flipping mare de biți.
#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
}
Având în vedere această funcție care lucrează pentru un singur vector, putem spune că suntem într-o buclă pentru a procesa un șir întreg. Când ne-am're deja de direcționare SSE2, putem face o vectorizate de sfârșit de șir verifica în același timp.
Putem face, de asemenea, mult mai bine pentru "curatare" de la ultimul up-to-15 bytes rămase după vectorii de 16B: superioare-carcasa este idempotent, atât de re-prelucrare unele de intrare de octeți este bine. Facem un neutru sarcină de ultima 16B de la sursă, și păstrați-l în dest tampon se suprapun ultima 16B magazin din bucla.
Singura dată când acest lucru nu't lucru este atunci când întregul șir este sub 16B: Chiar și atunci când dst=src, non-atomice citire-modificare-scriere este *nu* același lucru ca nu se ating unele bytes, la toate, și poate rupe multithreaded cod. Avem un scalar, bucla pentru că, și, de asemenea, pentru a obține
srcaliniate. Când ne-am don't știu unde încheiere 0 va fi, un neutru se încarcă de la
src` ar putea trece în pagina următoare și segfault. Dacă avem nevoie de octeți într-un aliniat 16B bucată, l's întotdeauna în condiții de siguranță pentru a încărca întregul aliniat 16B bucată.
Sursa completa: în github gist.
// 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;
}
Ori pentru 40M iterații, pe un Core2 (Merom) de 2.4 GHz. gcc 5.2 -O3 -martie=nativ
. (Ubuntu 15.10). dst != src
(deci, vom face o copie), dar acestea nu't se suprapun (și nu't apropiere). Ambele sunt aliniate.
_mm_storeu
, pentru că storeu este mai lent pe Merom chiar și atunci când adresa este aliniat. L's bine pe Nehalem și mai târziu. Am'am lăsat, de asemenea, codul de drept-este de acum, în loc să repare eșecul de a copia de încheiere 0, în unele cazuri, pentru că eu nu't doriți să re-timp totul.)
Deci, pentru scurt siruri de caractere mai mult decât 16B, acest lucru este dramatic mai rapid decât auto-vectorizate. Lungimi unul-mai-mult-o-vector-lățime don't prezintă o problemă. Acestea ar putea fi o problemă atunci când operează într-un loc, pentru că de un magazin-transmiterea stand. (Dar rețineți că l's încă mai bine pentru a procesa propriile noastre ieșire, mai degrabă decât cel inițial, deoarece toupper este idempotent).
Nu's o mulțime de posibilități de tuning pentru diferite cazuri de utilizare, în funcție de ceea ce înconjoară codul vrea, și ținta microarhitectura. Obtinerea compilatorul să emită cod frumos pentru curățire parte este dificil. Folosind ffs(3)
(care compilează bsf sau tzcnt pe x86) pare a fi bun, dar evident că putin are nevoie de o re-cred că de când am observat un bug după ce a scris cel mai mult de acest răspuns (a se vedea FIXME comentarii).
Vector speedups pentru chiar mai mici siruri de caractere pot fi obținute cu movq " sau " movd` sarcini/magazine. Personaliza după cum este necesar pentru caz de utilizare. ora de vară punct poate avansa către o altă sumă decât
srcpointer, dar odată ce ajungem la un aliniat
srcpointer, am'll doar faci neutru vector magazine la
ora de vară`.
Pentru textul pe care's UTF-8, dar cea mai mare parte este format din ASCII subset de UTF-8, acest lucru poate fi bun: de înaltă performanță, în caz comun cu comportamentul corect în toate cazurile. Atunci când nu's o mulțime de non-ASCII, acesta va fi, probabil, mai rău decât să stai în scalar UTF-8 conștienți buclă tot timpul.
Efectuarea engleză mai rapid în detrimentul altor limbi nu este un viitor decizie dacă dezavantajul este semnificativă. În turcești locale (tr_TR
), rezultatul corect la toupper('i') "este" 'I'(U0130), nu'I'
(ASCII). A se vedea Martin Bonner's comentarii la o întrebare despre pentru a reduce()
a fi lent pe Windows.
Putem verifica, de asemenea, o excepție-o listă și o rezervă de scalare acolo, ca pentru multi-byte UTF-8 caractere de intrare.
Cu această complexitate, SSE4.2 PCMPISTRM
sau ceva s-ar putea fi capabil să facă o mulțime de controale într-un du-te.
Ai ASCII sau caractere Internaționale în siruri de caractere?
Daca's cel din urmă caz, "uppercasing" nu este atât de simplu, și depinde de folosit alfabetul. Există bicameral și unicameral alfabete. Numai bicameral alfabete au caractere diferite de litere mari și mici. De asemenea, sunt compuse de caractere, cum ar fi limba latină scrisoare de capital 'DZ' (\u01F1 'DZ'), care utilizează așa-numitele titlul caz. Acest lucru înseamnă că numai primul caracter (D) devine schimbat.
Iti sugerez sa te uiti în ICU, și diferența dintre Simplă și Completă Cazul Asocierilor. Acest lucru ar putea ajuta:
string StringToUpper(string strToConvert)
{
for (std::string::iterator p = strToConvert.begin(); strToConvert.end() != p; ++p)
*p = toupper(*p);
return p;
}
Sau,
string StringToUpper(string strToConvert)
{
std::transform(strToConvert.begin(), strToConvert.end(), strToConvert.begin(), ::toupper);
return strToConvert;
}
Următoarele lucrări pentru mine.
#include <algorithm>
void toUpperCase(std::string& str)
{
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
}
int main()
{
std::string str = "hello";
toUpperCase(&str);
}
Unul mai rapid dacă utilizați numai caractere ASCII:
for(i=0;str[i]!=0;i++)
if(str[i]<='z' && str[i]>='a')
str[i]-=32;
Vă rugăm să rețineți că acest cod rula mai repede, dar funcționează numai pe ASCII și nu este o "rezumat" soluție.
Dacă ai nevoie de UNICODE soluții sau mai convențional și abstract soluții, du-te pentru alte răspunsuri și de a lucra cu metode de C++ siruri de caractere.
Atâta timp cât ești bine cu ASCII-doar și poate furniza o adresă pointer la RW memorie, nu este un simplu și foarte eficient-o linie în C:
void strtoupper(char* str)
{
while (*str) *(str++) = toupper((unsigned char)*str);
}
Acest lucru este deosebit de bun pentru simplu ca siruri de caractere ASCII identificatori care doriți să se normalizeze în același caracter caz. Apoi, puteți utiliza tampon pentru a construi un std:string exemplu.
//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;
}
#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());
Aceasta va efectua mai bine decât toate răspunsurile care folosesc globale toupper funcție, și este, probabil, ceea ce boost::to_upper este de a face dedesubt.
Acest lucru este pentru că ::toupper are să se uite în sus locale - pentru că s-ar putea'am fost schimbat de către un alt thread pentru fiecare invocare, întrucât aici doar apelul la localizare() a această pedeapsă. Și se uită în sus locale, în general, implică luarea un sistem de blocare.
Acest lucru, de asemenea, funcționează cu C++98 după înlocuirea automată, utilizarea de noi, non-const str.date(), și a adăuga un spațiu pentru a sparge șablonul de închidere (">>" pentru "> >") astfel:
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());
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 );
încercați toupper () funcția (
#includ <ctype.h>`). acceptă caractere ca argumente, siruri de caractere sunt formate din caractere, deci'll trebuie să itera peste fiecare caracter individual, care atunci când sunt puse împreună, formează șirul
nu sunt sigur există o construit în funcție. Încercați acest lucru:
Include fie ctype.h SAU cctype biblioteci, precum și stdlib.h ca parte a directivelor preprocesor.
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
}
Soluția mea (compensare 6 biți pentru 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++;
}
}