Jak można przekonwertować ciąg na wielkie litery. Przykłady, które znalazłem w Google, dotyczą tylko znaków.
268
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");
::toupper
najprawdopodobniej przyjmuje ASCII.std::string newstr(boost::to_upper_copy<std::string>("Hello World"));
źródło
toupper()
może być zaimplementowany jako makro. Może to powodować problem.toupper
. Jakieś pomysły?Krótkie rozwiązanie przy użyciu C ++ 11 i toupper ().
źródło
c
byłbyconst char
typu (odauto
)? Jeśli tak, nie możesz przypisać go (z powoduconst
części) do tego, co zwracatoupper(c)
.c
należy go rzucić,unsigned char
aby został skorygowany.Uwaga: kilka problemów z najlepszym rozwiązaniem:
Co oznacza, że
cctype
czł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
char
podpisany 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.)źródło
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=native
na 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 zLANG=C
i zLANG=en_CA.UTF-8
.Nie testowałem przy użyciu RangeT innego niż std :: string. Może inna forma
to_upper_copy
optymalizacji lepiej, ale myślę, że zawsze będzienew
/malloc
miejsce 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 odstd::string
i pisanie dochar dstbuf[4096]
sensownego do testowania.Wywołanie pętli glibc
toupper
: 6,67s (nie sprawdzaint
wyniku dla potencjalnego wielobajtowego UTF-8. To ma znaczenie dla tureckiego.)cmov
, a tabela w każdym razie jest gorąca w L1.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
-O3
włą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. Wynikperf record
/report
(dlacycles
zdarzenia perf) to: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
strlen
pierwszego. Nie będzie to konieczne w przypadku ciągów o wyraźnej długości (takich jak C ++std::string
).Przyzwoite libc będzie miało wydajność
strlen
znacznie 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.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 <= high
pojedynczego porównania bez znaku poprzez przesunięcie zakresu, dzięki czemu dowolna wartość mniejsza niżlow
wraca do wartości większej niżhigh
. (Działa to, jeślilow
ihigh
nie 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.
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=src
nieatomowy 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
src
wyrównania. Ponieważ nie wiemy, gdzie będzie kończące się 0, niezaangażowane ładowanie zsrc
moż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 .
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.(Właściwie taktowane
_mm_store
w 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ą
movq
lubmovd
ł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.
dst
Punkt może przejść przez inną kwotę niżsrc
wskaźnik, ale kiedy wrócimy do wyrównanegosrc
wskaźnika, będziemy nadal tak robić aligné sklepów wektorowejdst
.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 wyniktoupper('i')
to'İ'
(U0130), a nie'I'
(zwykły ASCII). Zobacz komentarze Martina Bonnera dotyczące pytania otolower()
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
PCMPISTRM
lub coś może być w stanie wykonać wiele naszych kontroli za jednym razem.źródło
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
źródło
Lub,
źródło
**
po parametrach pierwszego rozwiązania?**
jest to literówka, która pozostała po próbie użycia pogrubionej czcionki w składni kodu.toupper
jest wywoływany z liczbami ujemnymi.Poniższe działa dla mnie.
źródło
toupper
jest wywoływany z liczbami ujemnymi.Użyj lambda.
źródło
Szybszy, jeśli używasz tylko znaków ASCII :
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 ++.
źródło
C++
, aleC
tutaj napisałeś odpowiedź. (Nie jestem jednym z downvoters.)'
?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:
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.
źródło
źródło
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.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:
źródło
źródło
reserve
iback_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; }
źródło
toupper
jest wywoływany z liczbami ujemnymi.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źródło
toupper
jest wywoływana z liczbami ujemnymi. Powinieneś był wspomnieć o niezbędnej obsadzieunsigned char
.Oto najnowszy kod z C ++ 11
źródło
toupper
jest wywoływany z liczbami ujemnymi.Korzystanie z Boost.Text, który będzie działał dla tekstu Unicode
źródło
Odpowiedź od @dirkgently jest bardzo inspirujący, ale chcę podkreślić, że ze względu na obawy, jak pokazano poniżej,
Prawidłowe użycie
std::toupper
powinno być:Wynik:
źródło
nie jestem pewien, czy istnieje wbudowana funkcja. Spróbuj tego:
Dołącz biblioteki ctype.h OR cctype oraz stdlib.h jako część dyrektyw preprocesora.
źródło
toupper
jest wywoływany z liczbami ujemnymi.Moje rozwiązanie (wyczyszczenie szóstego bitu dla alfa):
źródło
toupper
jest wywoływany z liczbami ujemnymi.Wszystkie te rozwiązania na tej stronie są trudniejsze niż muszą.
Zrób to
RegName
jest twójstring
. Uzyskaj rozmiar łańcucha nie używaj gostring.size()
jako rzeczywistego testera, jest bardzo nieuporządkowany i może powodować problemy. następnie. najbardziej podstawowafor
pę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ć
źródło
tolower
pętli, a większość z nich używa standardowych nazw zmiennych pętlii
, a nie dziwnychforLoop
.Bez użycia bibliotek:
źródło
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.
z przypadkiem użycia:
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
źródło
źródło
Ta funkcja c ++ zawsze zwraca ciąg wielkich liter ...
źródło
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.
źródło
I know you're not supposed to modify that data area
- jaki obszar danych nie należy modyfikować?str[i] = toupper(str[i]);
idealnie w porządku (cóż, niezupełnie w porządku, ale naprawia większość rzeczy źle).