Konwertuj ciąg znaków w C ++ na wielkie litery

268

Jak można przekonwertować ciąg na wielkie litery. Przykłady, które znalazłem w Google, dotyczą tylko znaków.

OrangeAlmondSoap
źródło

Odpowiedzi:

205

Algorytmy ciągu wzmocnionego:

#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
źródło
5
Ma to również tę zaletę, że i18n, gdzie ::touppernajprawdopodobniej przyjmuje ASCII.
Ben Straub,
4
Twoja ostatnia linia się nie kompiluje - musisz zmienić na coś takiego:std::string newstr(boost::to_upper_copy<std::string>("Hello World"));
maxschlepzig
58
nie powinna to być akceptowana odpowiedź, ponieważ wymaga wzmocnienia lub tytuł powinien zostać zmieniony.
Andrea,
44
tak, zamierzam zainstalować boost tylko dla to_upper ... świetny pomysł! </ sarkazm> :)
thang
12
Osobiście nie jestem skłonny do wzmocnienia jako odpowiedzi na „jak zrobić x w C ++?” ponieważ boost nie jest wcale lekkim rozwiązaniem. Wygląda na to, że albo kupujesz boost jako framework (lub ACE, Qt lub Recusion ToolKit ++ lub ...), albo nie. Wolałbym zobaczyć rozwiązania językowe.
jwm
486
#include <algorithm>
#include <string>

std::string str = "Hello World";
std::transform(str.begin(), str.end(),str.begin(), ::toupper);
Pierre
źródło
8
W rzeczywistości toupper()może być zaimplementowany jako makro. Może to powodować problem.
bezpośrednio
3
bind (:: toupper, build <unsigned char> (_ 1)) z boost.lambda będzie działał idealnie dobrze, myślę.
Johannes Schaub - litb
11
Podejście to działa dobrze w przypadku ASCII, ale zawiesza się w przypadku kodowania znaków wielobajtowych lub specjalnych reguł dotyczących obudów, takich jak niemiecki „ß”.
dan04
9
Zmieniłem zaakceptowaną odpowiedź na tę, która używa bibliotek doładowań, ponieważ była szybsza (w moich nieformalnych testach), łatwiejsza w użyciu i nie ma problemów związanych z tym rozwiązaniem. Nadal dobre rozwiązanie dla przypadków, w których nie można użyć wzmocnienia.
OrangeAlmondSoap
2
Nie mogę zrozumieć, dlaczego kompilator odrzuca wcześniej ten kod bez kwalifikatora :: toupper. Jakieś pomysły?
sasha.sochka
89

Krótkie rozwiązanie przy użyciu C ++ 11 i toupper ().

for (auto & c: str) c = toupper(c);
Thanasis Papoutsidakis
źródło
Nie cbyłby const chartypu (od auto)? Jeśli tak, nie możesz przypisać go (z powodu constczęści) do tego, co zwraca toupper(c).
PolGraphic,
5
@PolGraphic: Oparty na zakresie do użycia metod begin () / end () kontenera do iteracji po jego zawartości. std :: basic_string ma zarówno const, jak i zmienny iterator (zwracane odpowiednio przez cbegin () i begin (), patrz std :: basic_string :: begin ), więc dla (:) używa odpowiedniego (cbegin (), jeśli str jest zadeklarowana stała, z auto =: = const char, begin () w przeciwnym razie, z auto =: = char).
Thanasis Papoutsidakis
5
Zobacz anser dirkgently poniżej, cnależy go rzucić, unsigned charaby został skorygowany.
Cris Luengo,
boost to_upper () wydaje się o wiele bardziej zgodny z funkcjami c ++ STL niż 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());

Uwaga: kilka problemów z najlepszym rozwiązaniem:

21.5 Narzędzia sekwencyjne zakończone zerem

Treść tych nagłówków powinna być taka sama, jak nagłówki Standardowej biblioteki C <ctype.h>, <wctype.h>, <string.h>, <wchar.h> i <stdlib.h> [...]

  • Co oznacza, że cctypeczłonkami mogą być makra nieodpowiednie do bezpośredniego wykorzystania w standardowych algorytmach.

  • Innym problemem związanym z tym samym przykładem jest to, że nie rzuca on argumentu ani nie weryfikuje, czy nie jest on ujemny; jest to szczególnie niebezpieczne dla systemów, w których charpodpisany jest zwykły . (Powodem jest to, że jeśli jest to zaimplementowane jako makro, prawdopodobnie użyje tabeli odnośników, a twój argument będzie indeksował tę tabelę. Ujemny indeks da ci UB.)

bezpośrednio
źródło
Normalne elementy cctype to makra. Pamiętam, że czytałem, że one także musiały być funkcjami, chociaż nie mam kopii standardu C90 i nie wiem, czy zostało to wyraźnie określone, czy nie.
David Thornley,
1
muszą być funkcjami w C ++ - nawet jeśli C pozwala im być makrami. zgadzam się jednak z twoim drugim punktem na temat castingu. najlepsze rozwiązanie może przekazać wartości ujemne i spowodować UB. to jest powód, dla którego nie głosowałem (ale też nie głosowałem) :)
Johannes Schaub - litb
1
nie można pominąć standardowego cytatu: 7.4.2.2/1 (słaby litb, który odnosi się tylko do wersji roboczej C99 TC2) i C ++ 17.4.1.2/6 w chwale standardu c ++ 98.
Johannes Schaub - litb
1
(uwaga dopiskiem na to: „To uniemożliwia powszechną praktyką zapewniając maskowanie makro .... bla blupp .. Jedynym sposobem, aby to zrobić w C ++ jest dostarczenie extern funkcji inline.”) :)
Johannes Schaub - litb
1
... osiągnięto dzięki tej sztuczce: stackoverflow.com/questions/650461/...
Johannes Schaub - litb
27

Ten problem można rozwiązać za pomocą SIMD dla zestawu znaków ASCII.


Porównanie przyspieszeń:

Wstępne testy z x86-64 gcc 5.2 -O3 -march=nativena Core2Duo (Merom). Ten sam ciąg 120 znaków (mieszane małe i małe litery ASCII), przekonwertowany w pętli 40 mln razy (bez wstawiania pliku krzyżowego, więc kompilator nie może zoptymalizować ani wyciągnąć żadnego z niego z pętli). Te same bufory źródłowy i docelowy, więc nie ma narzutu malloc ani efektów pamięci / pamięci podręcznej: dane są gorące w pamięci podręcznej L1 przez cały czas, a my jesteśmy całkowicie związani z procesorem.

  • boost::to_upper_copy<char*, std::string>(): 198,0 . Tak, Boost 1.58 na Ubuntu 15.10 jest naprawdę tak powolny. Profilowałem i jednym krokiem asm w debuggerze, i to jest naprawdę bardzo złe: jest dynamiczny rzut zmiennej regionalnej na znak !!! (dynamic_cast przyjmuje wiele wywołań do strcmp). Dzieje się tak z LANG=Ci z LANG=en_CA.UTF-8.

    Nie testowałem przy użyciu RangeT innego niż std :: string. Może inna formato_upper_copy optymalizacji lepiej, ale myślę, że zawsze będzie new/ mallocmiejsce na kopię, więc trudniej jest przetestować. Może coś, co zrobiłem, różni się od zwykłego przypadku użycia, a może normalnie zatrzymany g ++ może wyciągnąć elementy ustawień regionalnych z pętli dla poszczególnych znaków. Moje czytanie w pętli od std::stringi pisanie do char dstbuf[4096]sensownego do testowania.

  • Wywołanie pętli glibc toupper: 6,67s (nie sprawdza intwyniku dla potencjalnego wielobajtowego UTF-8. To ma znaczenie dla tureckiego.)

  • Pętla tylko ASCII: 8,79s (moja podstawowa wersja dla wyników poniżej.) Najwyraźniej wyszukiwanie w tabeli jest szybsze niż a cmov, a tabela w każdym razie jest gorąca w L1.
  • Automatyczna wektoryzacja tylko ASCII: 2,51s . (120 znaków jest w połowie drogi między najgorszym i najlepszym przypadkiem, patrz poniżej)
  • Wektoryzacja tylko ASCII ręcznie: 1.35s

Zobacz także to pytanie dotyczące toupper()spowolnienia w systemie Windows, gdy ustawione są ustawienia regionalne .


Byłem zszokowany, że Boost jest o rząd wielkości wolniejszy niż inne opcje. Dokładnie sprawdziłem, czy mam -O3włączony, a nawet przeskoczyłem asm, aby zobaczyć, co robi. Jest prawie dokładnie taka sama prędkość z clang ++ 3.8. Ma ogromne obciążenie w pętli dla poszczególnych postaci. Wynik perf record/ report(dla cycleszdarzenia perf) to:

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

Autowektoryzacja

Gcc i clang będą automatycznie wektoryzować pętle tylko wtedy, gdy liczba iteracji jest znana przed pętlą. (tzn. pętle wyszukiwania, takie jak implementacja zwykłego C strlen, nie będą się automatycznie włączać).

Tak więc, dla łańcuchów wystarczająco małych, aby zmieściły się w pamięci podręcznej, otrzymujemy znaczne przyspieszenie dla łańcuchów ~ 128 znaków długo od strlenpierwszego. Nie będzie to konieczne w przypadku ciągów o wyraźnej długości (takich jak 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;
}

Przyzwoite libc będzie miało wydajność strlenznacznie szybszą niż zapętlanie bajtu naraz, więc oddzielne wektoryzowane pętle strlen i touper są szybsze.

Linia bazowa: pętla, która w locie sprawdza zakończenie 0.

Czasy dla 40M iteracji na Core2 (Merom) 2,4 GHz. gcc 5.2 -O3 -march=native. (Ubuntu 15.10). dst != src(więc tworzymy kopię), ale nie nakładają się (i nie są w pobliżu). Oba są wyrównane.

  • Ciąg 15 znaków: linia bazowa: 1,08s. autovec: 1.34s
  • Ciąg 16 znaków: linia bazowa: 1,16 s. autovec: 1,52s
  • Ciąg znaków 127: podstawowy: 8,91 s. autovec: 2.98s // czyszczenie poza wektorem ma 15 znaków do przetworzenia
  • Ciąg 128 znaków: linia bazowa: 9,00 s. autovec: 2,06s
  • Ciąg znaków 129: podstawowy: 9,04. autovec: 2.07s // czyszczenie inne niż wektorowe ma 1 znak do przetworzenia

Niektóre wyniki są nieco inne w przypadku clang.

Pętla microbenchmark, która wywołuje tę funkcję, znajduje się w osobnym pliku. W przeciwnym razie strlen()wyskoczy z pętli i zostanie wyciągnięty z pętli, i działa znacznie szybciej, szczególnie. dla 16 ciągów znaków (0,187).

Ma to tę główną zaletę, że gcc może automatycznie wektoryzować ją dla dowolnej architektury, ale główną wadą jest to, że jest wolniejsza w przypadku zwykle zwykłego przypadku małych łańcuchów.


Istnieją więc duże przyspieszenia, ale auto-wektoryzacja kompilatora nie tworzy świetnego kodu, szczególnie. do czyszczenia ostatnich do 15 znaków.

Ręczna wektoryzacja z elementami SSE:

Na podstawie mojej funkcji odwracania wielkości liter, która odwraca wielkość liter każdego znaku alfabetu. Korzysta z „sztuczki polegającej na porównywaniu bez znaku”, która umożliwia wykonanie low < a && a <= highpojedynczego porównania bez znaku poprzez przesunięcie zakresu, dzięki czemu dowolna wartość mniejsza niż lowwraca do wartości większej niż high. (Działa to, jeśli lowi highnie są zbyt daleko od siebie.)

SSE ma tylko podpisane porównanie-większe, ale nadal możemy użyć sztuczki „niepodpisane porównanie”, przesuwając zakres na dół podpisanego zakresu: Odejmij „a” + 128, więc znaki alfabetu mieszczą się w zakresie od -128 do -128 +25 (-128 + „z” - „a”)

Zauważ, że dodawanie 128 i odejmowanie 128 to to samo dla liczb całkowitych 8-bitowych. Nie ma dokąd pójść, więc jest to po prostu xor (dodawanie bez noszenia), przewracając wysoki bit.

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

Biorąc pod uwagę tę funkcję, która działa dla jednego wektora, możemy wywołać go w pętli w celu przetworzenia całego łańcucha. Ponieważ już celujemy w SSE2, możemy jednocześnie przeprowadzić wektoryzowaną kontrolę końca łańcucha.

Możemy również zrobić znacznie lepiej dla „czyszczenia” ostatnich do 15 bajtów pozostałych po wykonaniu wektorów 16B: górna obudowa jest idempotentna, więc ponowne przetwarzanie niektórych bajtów wejściowych jest w porządku. Wykonujemy nierównomierne ładowanie ostatniego 16B źródła i przechowujemy go w buforze docelowym pokrywającym ostatni sklep 16B z pętli.

Jedyny raz to nie działa, gdy cały ciąg jest poniżej 16B: Nawet gdy dst=srcnieatomowy odczyt-modyfikacja-zapis nie jest tym samym, co w ogóle nie dotykanie niektórych bajtów i może złamać wielowątkowy kod.

Mamy do tego pętlę skalarną, a także do srcwyrównania. Ponieważ nie wiemy, gdzie będzie kończące się 0, niezaangażowane ładowanie z srcmoże przejść do następnej strony i spowodować awarię. Jeśli potrzebujemy bajtów w wyrównanym fragmencie 16B, zawsze można bezpiecznie załadować cały wyrównany fragment 16B.

Pełne źródło: w 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;
}

Czasy dla 40M iteracji na Core2 (Merom) 2,4 GHz. gcc 5.2 -O3 -march=native. (Ubuntu 15.10). dst != src(więc tworzymy kopię), ale nie nakładają się (i nie są w pobliżu). Oba są wyrównane.

  • Ciąg 15 znaków: linia bazowa: 1,08s. autovec: 1.34s. instrukcja: 1.29s
  • Ciąg 16 znaków: linia bazowa: 1,16 s. autovec: 1,52s. manualnie: 0.335s
  • Ciąg 31 znaków: ręczny: 0,479s
  • Ciąg znaków 127: podstawowy: 8,91 s. autovec: 2,98s. manualnie: 0,925s
  • Ciąg 128 znaków: linia bazowa: 9,00 s. autovec: 2,06s. manualnie: 0,931s
  • Ciąg znaków 129: podstawowy: 9,04. autovec: 2,07s. instrukcja: 1,02s

(Właściwie taktowane _mm_storew pętli, nie _mm_storeu, ponieważ storeu działa wolniej na Meromie, nawet gdy adres jest wyrównany. Nie ma problemu na Nehalem i później. Zostawiłem też kod na razie, zamiast naprawiać błąd kopiowania kończące 0 w niektórych przypadkach, ponieważ nie chcę wszystkiego zmieniać na nowo.)

Tak więc w przypadku krótkich łańcuchów dłuższych niż 16B jest to znacznie szybsze niż wektoryzacja automatyczna. Długości o szerokości mniejszej niż wektor nie stanowią problemu. Mogą one stanowić problem podczas pracy w miejscu, z powodu przeciągnięcia spedycji. (Zauważ jednak, że nadal dobrze jest przetwarzać nasze własne dane wyjściowe, niż oryginalne dane wejściowe, ponieważ toupper jest idempotentny).

Istnieje wiele możliwości dostrojenia tego do różnych przypadków użycia, w zależności od tego, czego chce otaczający kod i docelowej mikroarchitektury. Nakłonienie kompilatora do wydania ładnego kodu dla części czyszczenia jest trudne. Używanie ffs(3)(które kompiluje się do bsf lub tzcnt na x86) wydaje się być dobre, ale oczywiście ten bit wymaga ponownego przemyślenia, ponieważ zauważyłem błąd po napisaniu większości tej odpowiedzi (patrz komentarze FIXME).

Przyspieszenia wektorowe dla jeszcze mniejszych ciągów można uzyskać za pomocą movqlub movdładunków / sklepów. Dostosuj odpowiednio do swojego przypadku użycia.


UTF-8:

Możemy wykryć, kiedy nasz wektor ma jakieś bajty z ustawionym wysokim bitem, i w takim przypadku wracamy do skalarnej pętli rozpoznającej utf-8 dla tego wektora. dstPunkt może przejść przez inną kwotę niż srcwskaźnik, ale kiedy wrócimy do wyrównanego srcwskaźnika, będziemy nadal tak robić aligné sklepów wektorowej dst.

W przypadku tekstu UTF-8, ale w większości składającego się z podzbioru ASCII UTF-8, może to być dobre: ​​wysoka wydajność we wspólnym przypadku z prawidłowym zachowaniem we wszystkich przypadkach. Jednak gdy jest dużo non-ASCII, prawdopodobnie będzie to gorsze niż pozostawanie w pętli rozpoznawania skalarnej UTF-8 przez cały czas.

Szybsze posługiwanie się językiem angielskim kosztem innych języków nie jest decyzją na przyszłość, jeśli wada jest znacząca.


Zorientowany na ustawienia regionalne:

W tureckich ustawieniach regionalnych ( tr_TR) poprawny wynik toupper('i')to 'İ'(U0130), a nie 'I'(zwykły ASCII). Zobacz komentarze Martina Bonnera dotyczące pytania o tolower()powolność w systemie Windows.

Możemy również sprawdzić listę wyjątków i wrócić do skalarnego, na przykład w przypadku wielobajtowych znaków wejściowych UTF8.

Przy takiej złożoności, SSE4.2 PCMPISTRMlub coś może być w stanie wykonać wiele naszych kontroli za jednym razem.

Peter Cordes
źródło
20

Czy masz ciągi znaków ASCII lub międzynarodowe?

W drugim przypadku „wielkie litery” nie są takie proste i zależą od użytego alfabetu. Istnieją alfabety dwuizbowe i jednoizbowe. Tylko dwuizbowe alfabety mają różne znaki dla wielkich i małych liter. Istnieją również znaki złożone, takie jak łacińska wielka litera „DZ” (\ u01F1 „DZ”), które używają tak zwanego tytułu . Oznacza to, że zmienia się tylko pierwszy znak (D).

Proponuję przyjrzeć się OIOM i różnicę między mapowaniem prostych i pełnych przypadków. To może pomóc:

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

Milan Babuškov
źródło
7
Lub niemiecki eszet (sp?), Rzecz, która wygląda jak grecka litera beta i oznacza „ss”. Nie ma jednego niemieckiego znaku, który oznaczałby „SS”, który jest odpowiednikiem wielkich liter. Niemieckie słowo „ulica”, gdy jest pisane dużymi literami, zyskuje jeden znak dłużej.
David Thornley,
6
Innym szczególnym przypadkiem jest grecka litera sigma (Σ), która ma dwie małe litery, w zależności od tego, czy jest na końcu słowa (ς), czy nie (σ). A potem są reguły specyficzne dla języka, jak na przykład turecki, w którym mapowanie spraw IRLı i Iİi.
dan04
1
„Górna obudowa” nazywa się składaniem skrzynek.
Columbo,
20
string StringToUpper(string strToConvert)
{
   for (std::string::iterator p = strToConvert.begin(); strToConvert.end() != p; ++p)
       *p = toupper(*p);

   return p;
}

Lub,

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

    return strToConvert;
}
użytkownik648545
źródło
4
jeśli nie masz dostępu do ulepszenia, drugie rozwiązanie jest prawdopodobnie najlepsze, jakie możesz uzyskać. co robią gwiazdy **po parametrach pierwszego rozwiązania?
Sam Brinck,
1
Jestem prawie pewien, że **jest to literówka, która pozostała po próbie użycia pogrubionej czcionki w składni kodu.
MasterHD
1
Ten kod wywołuje niezdefiniowane zachowanie, gdy toupperjest wywoływany z liczbami ujemnymi.
Roland Illig
17

Poniższe działa dla mnie.

#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
źródło
Zauważ, że std :: transform jest zdefiniowane w <algorytm>
edj
Tak. ten #
włącznik
1
Ten kod wywołuje niezdefiniowane zachowanie, gdy toupperjest wywoływany z liczbami ujemnymi.
Roland Illig
duplikat odpowiedzi udzielonej przez użytkownika648545 - -1
Piotr Dobrogost
@PiotrDobrogost Nie mam pojęcia o odpowiedzi udzielonej przez user648545. Nie skopiowałem tego. Kiedy porównuję dwie metody, sygnatura metody jest zupełnie inna, chociaż obie funkcje używają transformacji funkcji biblioteki.
Pabitra Dash
13

Użyj 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
źródło
2
Byron, nie martw się o inne komentarze. Odpowiadanie na stare pytania za pomocą nowego (nowoczesnego) rozwiązania jest całkiem w porządku.
Kyberias
13

Szybszy, jeśli używasz tylko znaków ASCII :

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

Pamiętaj, że ten kod działa szybciej, ale działa tylko na ASCII i nie jest rozwiązaniem „abstrakcyjnym”.

Jeśli potrzebujesz rozwiązań UNICODE lub bardziej konwencjonalnych i abstrakcyjnych, skorzystaj z innych odpowiedzi i pracuj z metodami napisów w C ++.

Luca C.
źródło
1
Pytanie jest oznaczone jako C++, ale Ctutaj napisałeś odpowiedź. (Nie jestem jednym z downvoters.)
hkBattousai
6
Napisałem odpowiedź C ORAZ odpowiedź C ++, ponieważ C ++ jest napisane, aby być w pełni kompatybilnym ze źródłami C, więc każde rozwiązanie C jest również poprawnym rozwiązaniem C ++
Luca C.
Ale o wiele lepiej jest udzielić odpowiedzi, która szanuje sposób C ++.
Dmitriy Yurchenko
Standardowym sposobem c ++ byłoby użycie std :: transform z toupper. To mniej kodu i na pewno przenośny. Ten kod opiera się na „fakcie”, że system użyje ascii jako mechanizmu kodowania znaków. Nie jestem pewien, czy wszystkie systemy są oparte na tym kodowaniu i dlatego nie jestem pewien, czy jest on przenośny.
AlexTheo
1
Dlaczego zdecydowałeś się użyć kodów ASCII zamiast znaków w nich zawartych '?
HolyBlackCat
11

Tak długo, jak nie przeszkadza ci tylko ASCII i możesz podać poprawny wskaźnik do pamięci RW, w C jest prosta i bardzo skuteczna jednowierszowa:

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

Jest to szczególnie przydatne w przypadku prostych ciągów znaków, takich jak identyfikatory ASCII, które chcesz znormalizować do tej samej wielkości znaków. Następnie możesz użyć bufora do skonstruowania instancji std: string.

k3a
źródło
Należy zauważyć, że ta odpowiedź jest dla ciągu ac zamiast std :: string
EvilTeach
Ma to oczywistą wadę bezpieczeństwa. Nie zrobiłbym tego
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
źródło
s.size () jest typu std :: size_t, który AFAIK mógłby
równie
Nie sądzę, aby istniały jakieś nowoczesne implementacje, w których podpisany jest wynik std :: string :: size. Biorąc pod uwagę, że zarówno semantycznie, jak i praktycznie, nie ma czegoś takiego jak rozmiar ujemny, zamierzam wybrać rozmiar_t będący co najmniej 32-bitową liczbą całkowitą bez znaku.
user1329482
Nie ma powodu, aby nie pisać for (size_t i = 0 .... Nie ma też żadnego powodu, aby utrudniać czytanie. To również najpierw kopiuje ciąg, a następnie zapętla go. Odpowiedź Łukasza jest lepsza pod pewnymi względami, z wyjątkiem tego, że nie wykorzystuje 'a'stałych postaci.
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());

Będzie to działać lepiej niż wszystkie odpowiedzi, które używają funkcji globalnego touppera, i prawdopodobnie jest to, co robi pod spacją :: to_upper.

Wynika to z tego, że :: toupper musi wyszukiwać ustawienia regionalne - ponieważ mogły zostać zmienione przez inny wątek - dla każdego wywołania, podczas gdy tutaj tylko wywołanie ustawienia narodowego () ma tę karę. A wyszukiwanie regionu zazwyczaj wymaga zablokowania.

Działa to również z C ++ 98 po zastąpieniu auto, użyciu nowego non-const str.data () i dodaniu spacji, aby przerwać zamykanie szablonu („>>” do „>>”) w następujący sposób:

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
źródło
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
źródło
nie poleciłby back_insertera, ponieważ znasz już długość; użyj wyniku std :: string (src.size ()); std :: transform (src.begin (), src.end (), result.begin (), up_char);
Viktor Sehr
Chociaż jestem pewien, że o tym wiesz.
Viktor Sehr
@Viktor Sehr, @bayda: Wiem, że to ma 2 lata, ale dlaczego nie skorzystać z tego, co najlepsze z obu światów. Użyj reservei back_inserter(dzięki czemu ciąg jest kopiowany tylko raz). 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);
Dmitrij Jurczenko
źródło
Ten kod wywołuje niezdefiniowane zachowanie, gdy toupperjest wywoływany z liczbami ujemnymi.
Roland Illig
2

spróbuj toupper()funkcji ( #include <ctype.h>). akceptuje znaki jako argumenty, ciągi znaków składają się ze znaków, więc będziesz musiał iterować każdy poszczególny znak, który po złożeniu składa się z ciągu

zmf
źródło
Ta sugestia wywołuje niezdefiniowane zachowanie, gdy toupperjest wywoływana z liczbami ujemnymi. Powinieneś był wspomnieć o niezbędnej obsadzie unsigned char.
Roland Illig
2

Oto najnowszy kod z C ++ 11

std::string cmd = "Hello World";
for_each(cmd.begin(), cmd.end(), [](char& in){ in = ::toupper(in); });
użytkownik2787620
źródło
Ten kod wywołuje niezdefiniowane zachowanie, gdy toupperjest wywoływany z liczbami ujemnymi.
Roland Illig
1

Korzystanie z Boost.Text, który będzie działał dla tekstu 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();
cor3ntin
źródło
1

Odpowiedź od @dirkgently jest bardzo inspirujący, ale chcę podkreślić, że ze względu na obawy, jak pokazano poniżej,

Podobnie jak wszystkie inne funkcje z, zachowanie std :: toupper jest niezdefiniowane, jeśli wartość argumentu nie jest reprezentowalna jako znak bez znaku ani równa EOF. Aby bezpiecznie korzystać z tych funkcji w przypadku zwykłych znaków (lub znaków podpisanych), argument należy najpierw przekonwertować na znak bez znaku
Odniesienie : std :: toupper

Prawidłowe użycie std::toupperpowinno być:

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

Wynik:

Hello world!
HELLO WORLD!
KaiserKatze
źródło
0

nie jestem pewien, czy istnieje wbudowana funkcja. Spróbuj tego:

Dołącz biblioteki ctype.h OR cctype oraz stdlib.h jako część dyrektyw preprocesora.

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
źródło
.length () nie jest typu „unsigned int”
mal
Ten kod wywołuje niezdefiniowane zachowanie, gdy toupperjest wywoływany z liczbami ujemnymi.
Roland Illig
0

Moje rozwiązanie (wyczyszczenie szóstego bitu dla 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++;
    }
}
Antonin GAVREL
źródło
Ten kod wywołuje niezdefiniowane zachowanie, gdy toupperjest wywoływany z liczbami ujemnymi.
Roland Illig
Nie ... Przed oddaniem głosu sprawdź, czy masz rację. Islower działałby tylko na wartościach ujemnych ...
Antonin GAVREL
-1

Wszystkie te rozwiązania na tej stronie są trudniejsze niż muszą.

Zrób to

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

RegNamejest twój string. Uzyskaj rozmiar łańcucha nie używaj go string.size()jako rzeczywistego testera, jest bardzo nieuporządkowany i może powodować problemy. następnie. najbardziej podstawowa forpętla.

pamiętaj, że rozmiar łańcucha zwraca również ogranicznik, więc użyj <, a nie <= w teście pętli.

wynik będzie: ciąg, który chcesz przekonwertować

sekret
źródło
4
Nie rozumiem, jak to jest prostsze niż rozwiązanie boost :: toupper. Czy możesz rozwinąć?
tr9sh
2
Istnieje już wiele prostych tolowerpętli, a większość z nich używa standardowych nazw zmiennych pętli i, a nie dziwnych forLoop.
Peter Cordes,
-1

Bez użycia bibliotek:

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
źródło
Powyższy kod działa tylko w przypadku kodowań zgodnych z ASCII. Ani pytanie, ani twoja odpowiedź nie wspomina o tym ograniczeniu. Jeden z nich powinien.
Roland Illig
-1

Jeśli zajmujesz się tylko 8-bitowymi znakami (które zakładają wszystkie inne odpowiedzi oprócz Milana Babuškova), możesz uzyskać największą prędkość, generując tablicę przeglądową w czasie kompilacji za pomocą metaprogramowania. Na ideone.com działa to 7 razy szybciej niż funkcja biblioteki i 3 razy szybciej niż wersja napisana ręcznie ( http://ideone.com/sb1Rup ). Można go również dostosować za pomocą cech bez spowolnienia.

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

z przypadkiem użycia:

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

Aby uzyskać szczegółową (wiele stron) opis tego, jak to działa, mogę bezwstydnie podłączyć mojego bloga: http://metaporky.blogspot.de/2014/07/part-4-generating-look-up-tables-at.html

odinthenerd
źródło
-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
źródło
-1

Ta funkcja c ++ zawsze zwraca ciąg wielkich liter ...

#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
źródło
-3

Korzystam z tego rozwiązania. Wiem, że nie powinieneś modyfikować tego obszaru danych ... ale myślę, że dotyczy to głównie błędów przepełnienia bufora i znaku zerowego ... górna część obudowy nie jest taka sama.

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
źródło
I know you're not supposed to modify that data area- jaki obszar danych nie należy modyfikować?
user93353
3
Jest późno, ale co u licha? Ta zwariowana linia może zostać zastąpiona str[i] = toupper(str[i]);idealnie w porządku (cóż, niezupełnie w porządku, ale naprawia większość rzeczy źle).
Chris