Jak przekonwertować std :: string na małe litery?

777

Chcę przekonwertować std::stringna małe litery. Zdaję sobie sprawę z tej funkcji tolower(), jednak w przeszłości miałem problemy z tą funkcją i nie jest ona idealna, ponieważ używa się jej zstd::string wymagałoby iteracji nad każdą postacią.

Czy istnieje alternatywa, która działa w 100% przypadków?

Konrad
źródło
34
Jak inaczej przekonwertowałbyś każdy element listy czegokolwiek na coś innego, bez iteracji po liście? Łańcuch jest tylko listą znaków, jeśli musisz zastosować jakąś funkcję do każdego znaku, będziesz musiał iterować przez ciąg. Nie ma mowy o tym.
14
Dlaczego dokładnie to pytanie obniża ocenę? Nie mam problemu z iteracją mojego ciągu, ale pytam, czy istnieją inne funkcje oprócz tolower (), toupper () itp.
Konrad
3
Jeśli masz tablicę znaków w stylu C, myślę, że możesz być w stanie dodać ox20202020 do każdego bloku 4 znaków (pod warunkiem, że WSZYSTKIE już są już wielkie), aby konwertować 4 znaki na małe litery na raz.
13
@ Dan: Jeśli mogą już być małymi literami, ale zdecydowanie są AZ lub az, możesz LUB z 0x20 zamiast dodawać. Jedna z tych tak inteligentnych, ale prawdopodobnie głupich optymalizacji, które prawie nigdy nie są tego warte ...
Steve Jessop
4
Nie wiem, dlaczego zostałby odrzucony ... z pewnością jest trochę dziwnie sformułowany (ponieważ musisz w jakiś sposób powtarzać każdy element), ale to ważne pytanie
warren

Odpowiedzi:

905

Na podstawie niezbyt często zadawanych pytań :

#include <algorithm>
#include <cctype>
#include <string>

std::string data = "Abc";
std::transform(data.begin(), data.end(), data.begin(),
    [](unsigned char c){ return std::tolower(c); });

Naprawdę nie uciekniesz bez iteracji po każdej postaci. Nie ma sposobu, aby dowiedzieć się, czy postać jest pisana małymi lub dużymi literami.

Jeśli naprawdę nie cierpisz tolower(), oto specjalistyczna alternatywa tylko dla ASCII, której nie polecam używać:

char asciitolower(char in) {
    if (in <= 'Z' && in >= 'A')
        return in - ('Z' - 'z');
    return in;
}

std::transform(data.begin(), data.end(), data.begin(), asciitolower);

Należy pamiętać, że tolower()może to zrobić tylko substytucja na jeden bajt, co jest niewłaściwe dla wielu skryptów, zwłaszcza jeśli używa się kodowania wielobajtowego, takiego jak UTF-8.

Stefan Mai
źródło
25
(Być może stare, omawiane algorytmy niewiele się zmieniły) @Stefan Mai: Jakiego rodzaju „dużo narzutu” wywołuje algorytm STL? Funkcje są raczej ubogie (tj. Proste dla pętli) i często wbudowane, ponieważ rzadko wywołuje się wiele wywołań tej samej funkcji z tymi samymi parametrami szablonu w tej samej jednostce kompilacji.
eq-
257
Za każdym razem, gdy zakładasz, że postacie są ASCII, Bóg zabija kociaka. :(
Brian Gordon
13
Twój pierwszy przykład potencjalnie ma nieokreślone zachowanie (przejście chardo ::tolower(int).) Musisz upewnić się, że nie przejdziesz wartości ujemnej.
juanchopanza
37
-1 to użycie ::tolowermoże równie dobrze spowodować awarię, jest to UB dla danych innych niż ASCII.
Pozdrawiam i hth. - Alf
7
:: jest potrzebne przed tolower, aby wskazać, że znajduje się w najbardziej zewnętrznej przestrzeni nazw. Jeśli użyjesz tego kodu w innej przestrzeni nazw, może istnieć inna (prawdopodobnie niepowiązana) definicja tolowera, która ostatecznie byłaby preferencyjnie wybrana bez ::.
Charles Ofria
320

Zwiększenie zapewnia algorytm łańcuchowy do tego :

#include <boost/algorithm/string.hpp>

std::string str = "HELLO, WORLD!";
boost::algorithm::to_lower(str); // modifies str

Lub w przypadku braku miejsca :

#include <boost/algorithm/string.hpp>

const std::string str = "HELLO, WORLD!";
const std::string lower_str = boost::algorithm::to_lower_copy(str);
Obrabować
źródło
2
Zakładam, że nie ma takich samych problemów jak tolower z wejściem ASCII?
paulm
19
Nie działa dla ASCII-7.
DevSolar
1
Czy istnieje wersja tego nie na miejscu?
Ray
5
@Ray, tak,to_lower_copy
smac89
234

tl; dr

Użyj biblioteki ICU . Jeśli tego nie zrobisz, procedura konwersji po cichu załamie się na przypadkach, o których prawdopodobnie nawet nie wiesz.


Najpierw musisz odpowiedzieć na pytanie: jakie jest twoje kodowaniestd::string ? Czy to jest ISO-8859-1? A może ISO-8859-8? Lub Windows Codepage 1252? Czy to, czego używasz do konwersji wielkich i małych liter, wie o tym? (A może źle to kończy się w przypadku postaci 0x7f?)

Jeśli używasz UTF-8 (jedyny rozsądny wybór wśród kodowań 8-bitowych) z std::stringjako kontenerem, już oszukujesz siebie, aby uwierzyć, że nadal kontrolujesz rzeczy, ponieważ przechowujesz wielobajtową sekwencję znaków w kontenerze który nie zna koncepcji wielobajtowej. Nawet coś tak prostego jak .substr()tykająca kula czasowa. (Ponieważ podział sekwencji wielobajtowej spowoduje niepoprawny (pod-) ciąg znaków.)

I gdy tylko spróbujesz czegoś takiego std::toupper( 'ß' ), w jakimkolwiek kodowaniu, będziesz miał poważne kłopoty. (Ponieważ po prostu nie jest możliwe zrobienie tego „dobrze” ze standardową biblioteką, która może dostarczyć tylko jeden znak wyniku, a nie "SS"tutaj potrzebny.) [1] Innym przykładem byłby std::tolower( 'I' )inny wynik, w zależności od ustawień regionalnych . W Niemczech 'i'byłoby poprawne; w Turcji 'ı'(LATIN SMALL LETTER DOTLESS I) to oczekiwany wynik (który w kodowaniu UTF-8 to więcej niż jeden bajt). Jeszcze innym przykładem jest język grecki Sigma , wielkie litery '∑', małe litery 'σ'... z wyjątkiem końca słowa, gdzie jest 'ς'.

Więc, każda konwersja przypadku, która działa na znak na raz lub, co gorsza, bajt na raz, jest zepsuta przez projekt.

To jest sens, że standardowa biblioteka, po co to , zależnie od obsługiwanych ustawień narodowych, zależy od jest w stanie zrobić na komputerze, na którym działa twoje oprogramowanie ... i co robisz, jeśli nie jest?

Tak więc naprawdę szukasz klasy ciągów, która jest w stanie poradzić sobie z tym wszystkim poprawnie i nie jest to żaden z std::basic_string<>wariantów .

(Uwaga C ++ 11: std::u16stringi std::u32stringlepsze , ale wciąż nie są idealne. Przyniesiono C ++ 20std::u8string , ale wszystko to określa kodowanie. Pod wieloma innymi względami nadal nie znają mechaniki Unicode, takiej jak normalizacja, zestawianie, .. .)

Podczas gdy Boost wygląda ładnie, pod względem API, Boost.Locale jest zasadniczo otoczeniem ICU .Jeśli Boost jest skompilowany z obsługą ICU ... jeśli nie jest, Boost.Locale jest ograniczony do obsługi ustawień regionalnych skompilowanej dla standardowej biblioteki.

I uwierz mi że kompilacja Boosta na OIOM-ie może czasem być prawdziwym bólem. (Nie ma wstępnie skompilowanych plików binarnych dla systemu Windows, więc musisz je dostarczyć razem z aplikacją, a to otwiera zupełnie nową puszkę robaków ...)

Więc osobiście poleciłbym uzyskanie pełnego wsparcia Unicode prosto z pyska konia i korzystanie z OIOM biblioteki :

#include <unicode/unistr.h>
#include <unicode/ustream.h>
#include <unicode/locid.h>

#include <iostream>

int main()
{
    /*                          "Odysseus" */
    char const * someString = u8"ΟΔΥΣΣΕΥΣ";
    icu::UnicodeString someUString( someString, "UTF-8" );
    // Setting the locale explicitly here for completeness.
    // Usually you would use the user-specified system locale,
    // which *does* make a difference (see ı vs. i above).
    std::cout << someUString.toLower( "el_GR" ) << "\n";
    std::cout << someUString.toUpper( "el_GR" ) << "\n";
    return 0;
}

Skompiluj (z G ++ w tym przykładzie):

g++ -Wall example.cpp -licuuc -licuio

To daje:

ὀδυσσεύς

Zauważ, że konwersja Σ <-> σ w środku słowa, a konwersja Σ <-> ς na końcu słowa. Nie<algorithm> rozwiązanie nie może tego zapewnić.


[1] W 2017 r. Rada Ortografii Niemieckiej orzekła, że ​​„ẞ” U + 1E9E LATIN CAPITAL LETTER SHARP S może być oficjalnie używany jako opcja obok tradycyjnej konwersji „SS”, aby uniknąć dwuznaczności, np. W paszportach (gdzie nazwy są pisane wielkimi literami ). Mój piękny przykład, który stał się nieaktualny decyzją komisji ...

DevSolar
źródło
19
To jest poprawna odpowiedź w ogólnym przypadku. Standard nie daje nic do obsługi czegokolwiek oprócz „ASCII” oprócz kłamstw i oszustw. To sprawia, że myślisz, że możesz poradzić sobie z UTF-16, ale nie możesz. Jak mówi ta odpowiedź, nie można uzyskać właściwej długości znaków (nie bajtów) łańcucha UTF-16 bez obsługi własnego kodu Unicode. Jeśli masz do czynienia z prawdziwym tekstem, użyj ICU. Dzięki, @DevSolar
Ograniczone Zadośćuczynienie
Czy OIOM jest domyślnie dostępny w systemie Ubuntu / Windows lub musi zostać zainstalowany osobno? A co powiesz na tę odpowiedź: stackoverflow.com/a/35075839/207661 ?
Shital Shah
1
Hej, spójrz, prawdziwa odpowiedź! Dzięki, że wskazałeś mi właściwy kierunek, DevSolar.
Dan Bechard
2
@DevSolar uzgodnione! Pojęcie długości jest raczej pozbawione znaczenia dla tekstu (moglibyśmy dodać ligatury do listy przestępców). To powiedziawszy, ponieważ ponieważ ludzie są przyzwyczajeni do tabulacji i znaków kontrolnych zajmujących jedną jednostkę długości, punkty kodowe byłyby bardziej intuicyjną miarą. Aha, i dziękuję za udzielenie poprawnej odpowiedzi, przykro mi to widzieć tak daleko :-(
masaers
3
@LF Lepiej marginalnie. Ale tak wiele rzeczy wciąż nie jest objętych: toupperi tolowernadal działają na pojedyncze postacie. Klasa strun wciąż nie ma pojęcia normalizacji (np. Czy „ü” jest kodowane jako „u z diaeresisą” lub „u + diaeresis łączący”) lub gdzie łańcuch może być lub nie może być oddzielony. I tak dalej. łańcuch u8 jest (podobnie jak inne standardowe klasy łańcuchowe) odpowiedni do „przechodzenia”. Ale jeśli chcesz przetwarzać Unicode, potrzebujesz ICU.
DevSolar,
36

Korzystając z pętli C ++ 11 opartej na zakresie, prostszym kodem byłoby:

#include <iostream>       // std::cout
#include <string>         // std::string
#include <locale>         // std::locale, std::tolower

int main ()
{
  std::locale loc;
  std::string str="Test String.\n";

 for(auto elem : str)
    std::cout << std::tolower(elem,loc);
}
rany
źródło
9
Jednak na maszynie francuskiej program ten nie konwertuje znaków spoza ASCII dozwolonych w języku francuskim. Na przykład ciąg „Test String123. É Ï \ n ”zostanie przekonwertowany na: 'test string123. É Ï \ n ”, chociaż znaki É Ï i ich małe litery„ é ”i„ ï ”są dozwolone w języku francuskim. Wygląda na to, że żadne inne rozwiązanie tego wątku nie zapewniło takiego rozwiązania.
nacięcia
Myślę, że musisz ustawić odpowiednie ustawienia regionalne.
user1095108
@ incises, to ktoś opublikował odpowiedź na temat OIOM, i to z pewnością jest właściwy sposób. Łatwiej niż większość innych rozwiązań, które próbowałyby zrozumieć lokalizację.
Alexis Wilke,
Wolę nie używać bibliotek zewnętrznych, jeśli to możliwe, osobiście.
kayleeFrye_onDeck
15

Jest to kontynuacja odpowiedzi Stefana Mai: jeśli chcesz umieścić wynik konwersji w innym ciągu, musisz wcześniej przydzielić miejsce do przechowywania przed wywołaniem std::transform. Ponieważ STL przechowuje przekształcone znaki w docelowym iteratorze (zwiększając go przy każdej iteracji pętli), ciąg docelowy nie zostanie automatycznie zmieniony, a Ty ryzykujesz tupanie pamięci.

#include <string>
#include <algorithm>
#include <iostream>

int main (int argc, char* argv[])
{
  std::string sourceString = "Abc";
  std::string destinationString;

  // Allocate the destination space
  destinationString.resize(sourceString.size());

  // Convert the source string to lower case
  // storing the result in destination string
  std::transform(sourceString.begin(),
                 sourceString.end(),
                 destinationString.begin(),
                 ::tolower);

  // Output the result of the conversion
  std::cout << sourceString
            << " -> "
            << destinationString
            << std::endl;
}
użytkownik2218467
źródło
1
Dla mnie to nie
zmieniło
Przydałby się tu również iterator tylnego insertera zamiast ręcznej zmiany rozmiaru.
chili
11

Inne podejście wykorzystujące zakres oparty na pętli ze zmienną odniesienia

string test = "Hello World";
for(auto& c : test)
{
   c = tolower(c);
}

cout<<test<<endl;
Gilson PJ
źródło
6

O ile mi wiadomo, biblioteki Boost są naprawdę kiepskie pod względem wydajności. Przetestowałem ich nieuporządkowaną mapę do STL i była średnio 3 razy wolniejsza (najlepszy przypadek 2, najgorszy był 10 razy). Również ten algorytm wygląda zbyt nisko.

Różnica jest tak duża, że ​​jestem pewien, że jakikolwiek dodatek, który musisz zrobić, toloweraby zrównoważyć zwiększenie „na twoje potrzeby”, będzie znacznie szybszy niż doładowanie.

Zrobiłem te testy na Amazon EC2, dlatego wydajność była różna podczas testu, ale nadal masz pomysł.

./test
Elapsed time: 12365milliseconds
Elapsed time: 1640milliseconds
./test
Elapsed time: 26978milliseconds
Elapsed time: 1646milliseconds
./test
Elapsed time: 6957milliseconds
Elapsed time: 1634milliseconds
./test
Elapsed time: 23177milliseconds
Elapsed time: 2421milliseconds
./test
Elapsed time: 17342milliseconds
Elapsed time: 14132milliseconds
./test
Elapsed time: 7355milliseconds
Elapsed time: 1645milliseconds

-O2 zrobiło to tak:

./test
Elapsed time: 3769milliseconds
Elapsed time: 565milliseconds
./test
Elapsed time: 3815milliseconds
Elapsed time: 565milliseconds
./test
Elapsed time: 3643milliseconds
Elapsed time: 566milliseconds
./test
Elapsed time: 22018milliseconds
Elapsed time: 566milliseconds
./test
Elapsed time: 3845milliseconds
Elapsed time: 569milliseconds

Źródło:

string str;
bench.start();
for(long long i=0;i<1000000;i++)
{
    str="DSFZKMdskfdsjfsdfJDASFNSDJFXCKVdnjsafnjsdfjdnjasnJDNASFDJDSFSDNJjdsanjfsdnfjJNFSDJFSD";
    boost::algorithm::to_lower(str);
}
bench.end();

bench.start();
for(long long i=0;i<1000000;i++)
{
    str="DSFZKMdskfdsjfsdfJDASFNSDJFXCKVdnjsafnjsdfjdnjasnJDNASFDJDSFSDNJjdsanjfsdnfjJNFSDJFSD";
    for(unsigned short loop=0;loop < str.size();loop++)
    {
        str[loop]=tolower(str[loop]);
    }
}
bench.end();

Chyba powinienem przejść testy na dedykowanym komputerze, ale będę używać tego EC2, więc tak naprawdę nie muszę go testować na moim komputerze.

Etherealone
źródło
1
Czy otworzyłeś opcje optymalizacji podczas kompilacji? Myślę, że biblioteka doładowania STL powinna działać lepiej przy wysokim poziomie optymalizacji.
Wei Song,
1
Użyłem -O2 w jednym z testów i nic więcej.
Etherealone
2
Wydajność mapy nieuporządkowanej zależy od algorytmu mieszającego w połączeniu z używanymi danymi. Nie ma algorytmu haszowania magicznego, który działałby dla wszystkich danych, aby mapa nieuporządkowana była tak szybka, jak to możliwe. Benchmark i spróbuj różnych rzeczy. Powodem, dla którego osiągasz gorszą wydajność, jest to, że z haszem, którego używasz, dochodzi do wielu kolizji, co w zasadzie powoduje wyszukiwanie na liście. Sprawdź tę stronę, aby uzyskać więcej informacji: fgda.pl/post/7/gcc-hash-map-vs-unordered-map Dla moich celów funkcja podana w linku redukowała kolizje, a zatem była bardzo szybka.
leetNightshade
6

Najprostszym sposobem na konwersję łańcucha znaków na małą literę bez zawracania sobie głowy standardową przestrzenią nazw jest następująca

1: ciąg znaków z / bez spacji

#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
int main(){
    string str;
    getline(cin,str);
//------------function to convert string into lowercase---------------
    transform(str.begin(), str.end(), str.begin(), ::tolower);
//--------------------------------------------------------------------
    cout<<str;
    return 0;
}

2: ciąg bez spacji

#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
int main(){
    string str;
    cin>>str;
//------------function to convert string into lowercase---------------
    transform(str.begin(), str.end(), str.begin(), ::tolower);
//--------------------------------------------------------------------
    cout<<str;
    return 0;
}
Atul Rokade
źródło
5

std::ctype::tolower()ze standardowej biblioteki lokalizacji C ++ zrobi to za Ciebie poprawnie. Oto przykład wyodrębniony ze strony referencyjnej tolower

#include <locale>
#include <iostream>

int main () {
  std::locale::global(std::locale("en_US.utf8"));
  std::wcout.imbue(std::locale());
  std::wcout << "In US English UTF-8 locale:\n";
  auto& f = std::use_facet<std::ctype<wchar_t>>(std::locale());
  std::wstring str = L"HELLo, wORLD!";
  std::wcout << "Lowercase form of the string '" << str << "' is ";
  f.tolower(&str[0], &str[0] + str.size());
  std::wcout << "'" << str << "'\n";
}
Sameer
źródło
Fajnie, o ile możesz konwertować postacie na miejsce. Co jeśli łańcuch źródłowy jest const? Wydaje się, że sprawia to, że jest nieco bardziej niechlujny (np. Nie wygląda na to, że można go użyć f.tolower()), ponieważ musisz umieścić znaki w nowym ciągu. Czy użyłbyś transform()czegoś podobnego std::bind1st( std::mem_fun() )do operatora?
quazar
W przypadku ciągów stałych możemy po prostu utworzyć lokalną kopię, a następnie przekonwertować ją na miejscu.
Sameer
Tak, jednak wykonanie kopii powoduje dodatkowe obciążenie.
quazar
Możesz użyć std :: transform z wersją ctype :: tolower, która nie pobiera wskaźników. Użyj adaptera iteratora z tylnym modułem wstawiania, a nawet nie musisz się martwić o zmianę rozmiaru ciągu wyjściowego.
chili
Świetnie, zwłaszcza, że ​​w libstdc ++ tolowerz localeparametrem niejawne wywołanie funkcji use_facetwydaje się być wąskim gardłem wydajności. Jeden z moich współpracowników osiągnął kilkukrotny wzrost prędkości poprzez zastąpienie boost::iequals(która ma ten problem) wersją, która use_facetjest wywoływana tylko raz poza pętlą.
Arne Vogel
3

Alternatywą dla Boost jest POCO (pocoproject.org).

POCO oferuje dwa warianty:

  1. Pierwszy wariant tworzy kopię bez zmiany oryginalnego ciągu.
  2. Drugi wariant zmienia oryginalny ciąg znaków na miejscu.
    Wersje „In Place” zawsze mają w nazwie nazwę „InPlace”.

Obie wersje pokazano poniżej:

#include "Poco/String.h"
using namespace Poco;

std::string hello("Stack Overflow!");

// Copies "STACK OVERFLOW!" into 'newString' without altering 'hello.'
std::string newString(toUpper(hello));

// Changes newString in-place to read "stack overflow!"
toLowerInPlace(newString);
Jason Enochs
źródło
3

Istnieje sposób na konwersję wielkich liter na małe BEZ wykonywania testów , i jest to dość proste. Korzystanie z funkcji clocale.h przez funkcję isupper () / makro powinno zająć się problemami związanymi z twoją lokalizacją, ale jeśli nie, zawsze możesz dostosować UtoL [] do treści twojego serca.

Biorąc pod uwagę, że znaki C są w rzeczywistości 8-bitowymi liczbami całkowitymi (w tej chwili ignorując szerokie zestawy znaków), możesz utworzyć 256-bajtową tablicę zawierającą alternatywny zestaw znaków, aw funkcji konwersji użyj znaków w łańcuchu jako indeksów dolnych do tablica konwersji.

Zamiast mapowania 1 do 1, należy podać elementom tablicy wielkimi literami wartości BYTE int dla małych liter. Przydatne mogą być tutaj islower () i isupper () .

wprowadź opis zdjęcia tutaj

Kod wygląda następująco ...

#include <clocale>
static char UtoL[256];
// ----------------------------------------------------------------------------
void InitUtoLMap()  {
    for (int i = 0; i < sizeof(UtoL); i++)  {
        if (isupper(i)) {
            UtoL[i] = (char)(i + 32);
        }   else    {
            UtoL[i] = i;
        }
    }
}
// ----------------------------------------------------------------------------
char *LowerStr(char *szMyStr) {
    char *p = szMyStr;
    // do conversion in-place so as not to require a destination buffer
    while (*p) {        // szMyStr must be null-terminated
        *p = UtoL[*p];  
        p++;
    }
    return szMyStr;
}
// ----------------------------------------------------------------------------
int main() {
    time_t start;
    char *Lowered, Upper[128];
    InitUtoLMap();
    strcpy(Upper, "Every GOOD boy does FINE!");

    Lowered = LowerStr(Upper);
    return 0;
}

Takie podejście pozwoli jednocześnie na ponowne mapowanie dowolnych znaków, które chcesz zmienić.

Podejście to ma jedną ogromną zaletę w przypadku uruchamiania na nowoczesnych procesorach - nie ma potrzeby przewidywania rozgałęzień, ponieważ nie ma testów obejmujących rozgałęzienia. To oszczędza logikę przewidywania gałęzi CPU dla innych pętli i ma tendencję do zapobiegania utknięciu rurociągu.

Niektórzy tutaj mogą uznać to podejście za takie samo, jak w przypadku konwersji EBCDIC na ASCII.

użytkownik2548100
źródło
2
„Istnieje sposób na konwersję wielkich liter na małe BEZ wykonywania, jeśli testy” słyszałeś kiedyś o tablicach odnośników?
Gábor Buella
1
Niezdefiniowane zachowanie dla znaków ujemnych.
Roland Illig
Nowoczesne procesory mają wąskie gardło w pamięci, a nie w procesorze. Benchmarking byłby interesujący.
Contango
3

Ponieważ żadna z odpowiedzi nie wspomniała o nadchodzącej bibliotece Ranges, która jest dostępna w bibliotece standardowej od C ++ 20 i obecnie jest osobno dostępna w GitHub as range-v3, chciałbym dodać sposób przeprowadzenia tej konwersji przy użyciu tej biblioteki .

Aby zmodyfikować ciąg w miejscu:

str |= action::transform([](unsigned char c){ return std::tolower(c); });

Aby wygenerować nowy ciąg:

auto new_string = original_string
    | view::transform([](unsigned char c){ return std::tolower(c); });

(Nie zapomnij o #include <cctype>wymaganych nagłówkach zakresów.)

Uwaga: użycie unsigned charargumentu do lambda jest zainspirowane cppreferencją , która stwierdza:

Podobnie jak wszystkie inne funkcje z <cctype>, zachowanie nie std::tolowerjest zdefiniowane, jeśli wartość argumentu nie jest ani reprezentowalna, unsigned charani równa EOF. Aby bezpiecznie korzystać z tych funkcji w przypadku zwykłych charznaków ( signed chars), argument należy najpierw przekonwertować na unsigned char:

char my_tolower(char ch)
{
    return static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
}

Podobnie nie należy ich używać bezpośrednio ze standardowymi algorytmami, gdy typem wartości iteratora jest charlub signed char. Zamiast tego przekonwertuj wartość na unsigned charpierwszą:

std::string str_tolower(std::string s) {
    std::transform(s.begin(), s.end(), s.begin(), 
                // static_cast<int(*)(int)>(std::tolower)         // wrong
                // [](int c){ return std::tolower(c); }           // wrong
                // [](char c){ return std::tolower(c); }          // wrong
                   [](unsigned char c){ return std::tolower(c); } // correct
                  );
    return s;
}
LF
źródło
3

Moje własne funkcje szablonu, które wykonują wielkie / małe litery.

#include <string>
#include <algorithm>

//
//  Lowercases string
//
template <typename T>
std::basic_string<T> lowercase(const std::basic_string<T>& s)
{
    std::basic_string<T> s2 = s;
    std::transform(s2.begin(), s2.end(), s2.begin(), tolower);
    return std::move(s2);
}

//
// Uppercases string
//
template <typename T>
std::basic_string<T> uppercase(const std::basic_string<T>& s)
{
    std::basic_string<T> s2 = s;
    std::transform(s2.begin(), s2.end(), s2.begin(), toupper);
    return std::move(s2);
}
TarmoPikaro
źródło
Właśnie tego potrzebowałem. Właśnie użyłem towlowerszerokich znaków, które obsługują UTF-16.
Juv
2

Oto technika makro, jeśli chcesz czegoś prostego:

#define STRTOLOWER(x) std::transform (x.begin(), x.end(), x.begin(), ::tolower)
#define STRTOUPPER(x) std::transform (x.begin(), x.end(), x.begin(), ::toupper)
#define STRTOUCFIRST(x) std::transform (x.begin(), x.begin()+1, x.begin(),  ::toupper); std::transform (x.begin()+1, x.end(),   x.begin()+1,::tolower)

Należy jednak pamiętać, że komentarz @ AndreasSpindler do tej odpowiedzi nadal jest ważnym czynnikiem, jeśli pracujesz nad czymś, co nie jest tylko znakami ASCII.

Volomike
źródło
1
Głosuję za tym, aby podać makra, gdy istnieje idealnie dobre rozwiązanie - ty nawet dajesz te rozwiązania.
Jaśniejsze
2
Technika makr oznacza mniej pisania kodu dla czegoś, co często by się często używało w programowaniu. Dlaczego tego nie użyć? W przeciwnym razie, dlaczego w ogóle mają makra?
Volomike,
3
Makra są dziedzictwem z C, nad którym ciężko pracowano, aby się ich pozbyć. Jeśli chcesz zmniejszyć ilość pisania, użyj funkcji lub lambda. void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
Jaśniejsze
1
@Clearer Jeśli chcę być lepszym programistą, czy możesz podać mi linki do dokumentów ANSI, w których komisje ANSI C ++ mówią coś w stylu: „Musimy zwołać spotkanie, aby pozbyć się makr z C ++”? A może jakiś inny plan?
Volomike,
2
Nie mogę Jednak stanowisko Bjarne'a na ten temat było kilkakrotnie jasne. Poza tym istnieje wiele powodów, aby nie używać makr w C i C ++. xmoże być prawidłowym wyrażeniem, które po prostu kompiluje się poprawnie, ale daje całkowicie fałszywe wyniki z powodu makr.
Jaśniejsze
2
// tolower example (C++)
#include <iostream>       // std::cout
#include <string>         // std::string
#include <locale>         // std::locale, std::tolower

int main ()
{
  std::locale loc;
  std::string str="Test String.\n";
  for (std::string::size_type i=0; i<str.length(); ++i)
    std::cout << std::tolower(str[i],loc);
  return 0;
}

Aby uzyskać więcej informacji: http://www.cplusplus.com/reference/locale/tolower/

MoraRockey
źródło
2

Czy istnieje alternatywa, która działa w 100% przypadków?

Nie

Jest kilka pytań, które musisz sobie zadać przed wybraniem metody o małej obudowie.

  1. Jak kodowany jest ciąg? zwykły ASCII? UTF-8? jakaś forma rozszerzonego starszego kodowania ASCII?
  2. Co i tak rozumiesz przez małe litery? Reguły mapowania przypadków różnią się w zależności od języka! Czy chcesz coś zlokalizowanego w lokalizacji użytkownika? chcesz czegoś, co zachowuje się spójnie na wszystkich systemach, na których działa twoje oprogramowanie? Czy chcesz po prostu pisać małe znaki ASCII i przechodzić przez wszystko inne?
  3. Jakie biblioteki są dostępne?

Po uzyskaniu odpowiedzi na te pytania możesz zacząć szukać rozwiązania, które odpowiada Twoim potrzebom. Nie ma jednego uniwersalnego rozmiaru, który pasowałby do wszystkich na całym świecie!

płyn do płukania
źródło
2

Wypróbuj tę funkcję :)

string toLowerCase(string str) {
    int str_len = str.length();
    string final_str = "";
    for(int i=0; i<str_len; i++) {
        char character = str[i];
        if(character>=65 && character<=92) {
            final_str += (character+32);
        } else {
            final_str += character;
        }
    }
    return final_str;
}
BuSaeed
źródło
1

Na platformach Microsoft można korzystać z strlwrrodziny funkcji: http://msdn.microsoft.com/en-us/library/hkxwh33z.aspx

// crt_strlwr.c
// compile with: /W3
// This program uses _strlwr and _strupr to create
// uppercase and lowercase copies of a mixed-case string.
#include <string.h>
#include <stdio.h>

int main( void )
{
   char string[100] = "The String to End All Strings!";
   char * copy1 = _strdup( string ); // make two copies
   char * copy2 = _strdup( string );

   _strlwr( copy1 ); // C4996
   _strupr( copy2 ); // C4996

   printf( "Mixed: %s\n", string );
   printf( "Lower: %s\n", copy1 );
   printf( "Upper: %s\n", copy2 );

   free( copy1 );
   free( copy2 );
}
Samouk
źródło
0

Fragment kodu

#include<bits/stdc++.h>
using namespace std;


int main ()
{
    ios::sync_with_stdio(false);

    string str="String Convert\n";

    for(int i=0; i<str.size(); i++)
    {
      str[i] = tolower(str[i]);
    }
    cout<<str<<endl;

    return 0;
}
rashedcs
źródło
0

Skopiuj, ponieważ zabroniono poprawiania odpowiedzi. Dziękuję


string test = "Hello World";
for(auto& c : test)
{
   c = tolower(c);
}

Wyjaśnienie:

for(auto& c : test)jest tego rodzaju opartą na zakresie pętlą :
for (range_declaration:range_expression)loop_statement

  1. range_declaration: W auto& c
    tym przypadku automatyczny specyfikator służy do automatycznego odliczania typu. Zatem typ jest odejmowany od inicjalizatora zmiennych.

  2. range_expression: test
    Zakres w tym przypadku to znaki ciągu test.

Znaki ciągu testsą dostępne jako odniesienie wewnątrz pętli for poprzez identyfikator c.

zupa gulaszowa
źródło
Wyjaśnij, skąd skopiowałeś swoją odpowiedź.
bfontaine
0

C ++ nie ma zaimplementowanych dla łańcucha znaków metod tolower ani toupper, ale jest dostępny dla char. Można łatwo odczytać każdy znak ciągu, przekształcić go w wymaganą wielkość liter i umieścić z powrotem w ciągu. Przykładowy kod bez użycia biblioteki innej firmy:

#include<iostream>

int main(){
  std::string str = std::string("How IS The Josh");
  for(char &ch : str){
    ch = std::tolower(ch);
  }
  std::cout<<str<<std::endl;
  return 0;
}

Dla operacji na łańcuchach opartych na znakach : Dla każdego znaku w łańcuchu

Mahipal
źródło
-1

Może to być kolejna prosta wersja do konwersji wielkich liter na małe i odwrotnie. Użyłem wersji społeczności VS2017 do skompilowania tego kodu źródłowego.

#include <iostream>
#include <string>
using namespace std;

int main()
{
    std::string _input = "lowercasetouppercase";
#if 0
    // My idea is to use the ascii value to convert
    char upperA = 'A';
    char lowerA = 'a';

    cout << (int)upperA << endl; // ASCII value of 'A' -> 65
    cout << (int)lowerA << endl; // ASCII value of 'a' -> 97
    // 97-65 = 32; // Difference of ASCII value of upper and lower a
#endif // 0

    cout << "Input String = " << _input.c_str() << endl;
    for (int i = 0; i < _input.length(); ++i)
    {
        _input[i] -= 32; // To convert lower to upper
#if 0
        _input[i] += 32; // To convert upper to lower
#endif // 0
    }
    cout << "Output String = " << _input.c_str() << endl;

    return 0;
}

Uwaga: jeśli są znaki specjalne, należy je traktować za pomocą funkcji sprawdzania warunków.

Praveer Kumar
źródło
-8

Próbowałem std :: transform, wszystko, co dostaję, to obrzydliwy błąd kompilacji stl striptiz, który mogą zrozumieć tylko druidzi sprzed 200 lat (nie można przekonwertować z grypy na flibidi flabidi)

działa to dobrze i można je łatwo dostosować

string LowerCase(string s)
{
    int dif='a'-'A';
    for(int i=0;i<s.length();i++)
    {
        if((s[i]>='A')&&(s[i]<='Z'))
            s[i]+=dif;
    }
   return s;
}

string UpperCase(string s)
{
   int dif='a'-'A';
    for(int i=0;i<s.length();i++)
    {
        if((s[i]>='a')&&(s[i]<='z'))
            s[i]-=dif;
    }
   return s;
}
fdsfdsfdsfds
źródło