Como converter std :: string para minúsculas?

Eu quero converter um std::string para minúsculas. Estou ciente da function tolower() , no entanto, no passado eu tive problemas com esta function e dificilmente é ideal, de qualquer forma, como usar com um std::string exigiria iterar sobre cada caractere.

Existe uma alternativa que funciona 100% do tempo?

A partir disso :

 #include  #include  std::string data = "Abc"; std::transform(data.begin(), data.end(), data.begin(), ::tolower); 

Você não vai conseguir interferir em cada personagem. Não há como saber se o caractere é minúsculo ou maiúsculo.

Se você realmente odeia tolower() , aqui está uma alternativa não portátil que eu não recomendo que você use:

 char easytolower(char in) { if(in < = 'Z' && in >= 'A') return in - ('Z' - 'z'); return in; } std::transform(data.begin(), data.end(), data.begin(), easytolower); 

Esteja ciente de que ::tolower() só pode fazer uma substituição por caractere de byte único, o que é inadequado para muitos scripts, especialmente se estiver usando uma codificação de múltiplos bytes como UTF-8.

Existe um algoritmo de cadeia Boost para isso:

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

Ou, para não-no local:

 #include  const std::string str = "HELLO, WORLD!"; const std::string lower_str = boost::algorithm::to_lower_copy(str); 

tl; dr

Use a biblioteca do ICU .


Primeiro você tem que responder uma pergunta: Qual é a codificação do seu std::string ? É ISO-8859-1? Ou talvez ISO-8859-8? Ou página de código do Windows 1252? O que você está usando para converter letras maiúsculas para minúsculas sabe disso? (Ou falha miseravelmente por caracteres acima de 0x7f ?)

Se você estiver usando UTF-8 (a única escolha sã entre as codificações de 8 bits) com std::string como container, você já está enganando a si mesmo acreditando que ainda está no controle das coisas, porque você está armazenando um caractere multibyte seqüência em um contêiner que não está ciente do conceito de vários bytes. Até mesmo algo tão simples como .substr() é um timebomb. (Porque dividir uma seqüência multibyte resultará em uma (sub) string inválida.)

E assim que você tentar algo como std::toupper( 'ß' ) , em qualquer codificação, você está em apuros. (Porque simplesmente não é possível fazer isso “certo” com a biblioteca padrão, que pode entregar apenas um caractere de resultado, não o "SS" necessário aqui.) [1] Outro exemplo seria std::tolower( 'I' ) , que deve produzir resultados diferentes, dependendo da localidade . Na Alemanha, 'i' estaria correto; na Turquia, 'ı' (LATIN SMALL LETTER DOTLESS I) é o resultado esperado.

Então, há o ponto em que a biblioteca padrão depende de quais localidades são suportadas na máquina em que seu software está sendo executado … e o que você faz se não estiver?

Então, o que você está realmente procurando é uma class de strings capaz de lidar com tudo isso corretamente, e isso não é std::string .

(C ++ 11 note: std::u16string e std::u32string são melhores , mas ainda não são perfeitos.)

Enquanto Boost parece bom, API sábio, Boost.Locale é basicamente um wrapper em torno da UTI . Se o Boost for compilado com suporte ao ICU … se não for, o Boost.Locale é limitado ao suporte de locale compilado para a biblioteca padrão.

E acredite, obter Boost para compilar com a UTI pode ser uma dor real, às vezes. (Não há binários pré-compilados para o Windows, então você teria que fornecê-los junto com o seu aplicativo, e isso abre uma nova lata de worms …)

Então, pessoalmente, eu recomendaria obter suporte completo a Unicode diretamente da boca do cavalo e usar diretamente a biblioteca do ICU :

 #include  #include  #include  #include  int main() { char const * someString = "Eidenges\xe4\xdf"; icu::UnicodeString someUString( someString, "ISO-8859-1" ); // Setting the locale explicitly here for completeness. // Usually you would use the user-specified system locale. std::cout < < someUString.toLower( "de_DE" ) << "\n"; std::cout << someUString.toUpper( "de_DE" ) << "\n"; return 0; } 

Compile (com G ++ neste exemplo):

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

Isto dá:

 eidengesäß EIDENGESÄSS 

[1] Em 2017, o Conselho de Ortografia Alemã decidiu que "ẞ" U + 1E9E LETRA MAIÚSCULA SHARP S poderia ser usada oficialmente, como uma opção ao lado da tradicional conversão "SS" para evitar ambigüidade, por exemplo, em passaportes (onde os nomes são capitalizados ). Meu belo exemplo, tornado obsoleto pela decisão do comitê ...

Se a string contiver caracteres UTF-8 fora do intervalo ASCII, então boost :: algorithm :: to_lower não os converterá. Melhor usar boost :: locale :: to_lower quando o UTF-8 estiver envolvido. Veja http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/conversions.html

Usando o loop baseado em intervalo para C ++ 11, um código mais simples seria:

 #include  // std::cout #include  // std::string #include  // 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); } 

Este é um follow-up da resposta de Stefan Mai: se você gostaria de colocar o resultado da conversão em outra string, você precisa pré-alocar seu espaço de armazenamento antes de chamar std::transform . Como o STL armazena os caracteres transformados no iterador de destino (incrementando-o a cada iteração do loop), a string de destino não será automaticamente redimensionada e você corre o risco de ser interrompido pela memory.

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

Tanto quanto eu vejo bibliotecas Boost são realmente mau desempenho-wise. Eu testei o seu unordered_map para STL e foi em média 3 vezes mais lento (melhor caso 2, o pior foi 10 vezes). Também este algoritmo parece muito baixo.

A diferença é tão grande que tenho certeza de que qualquer acréscimo que você precise fazer para aumentar a tolower para aumentar “para as suas necessidades” será muito mais rápido do que aumentar.

Eu fiz esses testes em um Amazon EC2, portanto, o desempenho variou durante o teste, mas você ainda tem a idéia.

 ./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 fez assim:

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

Fonte:

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

Eu acho que deveria fazer os testes em uma máquina dedicada, mas vou usar este EC2, então eu realmente não preciso testá-lo na minha máquina.

std::ctype::tolower() da biblioteca de localização C ++ padrão fará isso corretamente para você. Aqui está um exemplo extraído da página de referência do tolower

 #include  #include  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::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"; } 

A maneira mais simples de converter a string em loweercase sem se preocupar com o namespace padrão é a seguinte

1: string com / sem espaços

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

2: string sem espaços

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

Outra abordagem usando o intervalo baseado em loop com a variável de referência

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

Uma alternativa ao Boost é o POCO (pocoproject.org).

O POCO fornece duas variantes:

  1. A primeira variante faz uma cópia sem alterar a string original.
  2. A segunda variante altera a string original no lugar.
    As versões “no local” sempre têm “InPlace” no nome.

Ambas as versões são demonstradas abaixo:

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

Existe uma maneira de converter letras maiúsculas para inferiores, sem fazer testes , e é bastante simples. A function isupper () / o uso do clocale.h pela macro deve resolver problemas relacionados à sua localização, mas se não, você pode sempre ajustar o UtoL [] para o conteúdo do seu coração.

Dado que os caracteres de C são realmente apenas ints de 8 bits (ignorando os conjuntos de caracteres largos para o momento) você pode criar uma matriz de 256 bytes segurando um conjunto alternativo de caracteres, e na function de conversão usar os caracteres em sua string como subscritos no matriz de conversão.

Em vez de um mapeamento 1 por 1, forneça aos membros da matriz maiúscula os valores BYTE int para os caracteres minúsculos. Você pode encontrar islower () e isupper () úteis aqui.

insira a descrição da imagem aqui

O código é assim …

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

Essa abordagem permitirá, ao mesmo tempo, remapear quaisquer outros caracteres que você deseje alterar.

Essa abordagem tem uma grande vantagem quando executada em processadores modernos, não há necessidade de fazer previsão de ramificação, pois não há testes se a ramificação. Isso salva a lógica de previsão de ramificação da CPU em outros loops e tende a impedir paralisações de pipeline.

Alguns aqui podem reconhecer essa abordagem como a mesma usada para converter EBCDIC em ASCII.

Aqui está uma técnica de macro se você quer algo simples:

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

No entanto, note que o comentário do @ AndreasSpindler sobre esta resposta ainda é uma consideração importante, no entanto, se você estiver trabalhando em algo que não seja apenas caracteres ASCII.

 // tolower example (C++) #include  // std::cout #include  // std::string #include  // std::locale, std::tolower int main () { std::locale loc; std::string str="Test String.\n"; for (std::string::size_type i=0; i 

Para mais informações: http://www.cplusplus.com/reference/locale/tolower/

Em plataformas da Microsoft, você pode usar a família de funções strlwr : 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  #include  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 ); } 

Fragmento de código

 #include using namespace std; int main () { ios::sync_with_stdio(false); string str="String Convert\n"; for(int i=0; i 

Use fplus :: to_lower_case ().

(fplus: https://github.com/Dobiasd/FunctionalPlus .

Pesquise ‘to_lower_case’ em http://www.editgym.com/fplus-api-search/ )

 fplus::to_lower_case(std::string("ABC")) == std::string("abc"); 

Copie porque não foi permitido melhorar a resposta. Obrigado SO


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

Explicação:

for(auto& c : test) é um loop baseado em intervalo do tipo
for ( range_declaration : range_expression ) loop_statement :

  1. range_declaration : auto& c
    Aqui, o especificador automático é usado para dedução de tipo automática. Então o tipo é deduzido do inicializador de variables.

  2. range_expression : test
    O intervalo, neste caso, são os caracteres do test de string.

Os caracteres do test string estão disponíveis como referência dentro do loop for através do identificador c .

Esta poderia ser outra versão simples para converter maiúsculas em minúsculas e vice-versa. Eu usei a versão da comunidade VS2017 para compilar este código-fonte.

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

Nota: se houver caracteres especiais, será necessário manipulá-lo usando a verificação de condição.

Eu tentei std :: transform, tudo que eu recebo é abominável stl criptic erro de compilation que só druids de 200 anos atrás pode entender (não é possível converter de flibidi flu flibidi)

isso funciona bem e pode ser facilmente ajustado

 string LowerCase(string s) { int dif='a'-'A'; for(int i=0;i='A')&&(s[i]< ='Z')) s[i]+=dif; } return s; } string UpperCase(string s) { int dif='a'-'A'; for(int i=0;i='a')&&(s[i]< ='z')) s[i]-=dif; } return s; } 
 //You can really just write one on the fly whenever you need one. #include  void _lower_case(std::string& s){ for(unsigned short l = s.size();l;s[--l]|=(1< <5)); } //Here is an example. //http://ideone.com/mw2eDK