Pergunta

Eu quero converter um std::string para minúsculas. Estou ciente da tolower() função, no entanto, as questões passado que tive com essa função e não é de qualquer maneira ideal como o uso com um std::string exigiria iteração sobre cada personagem.

Existe uma alternativa que funciona 100% do tempo?

Foi útil?

Solução

Não assim Perguntas frequentes :

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

Você realmente não vai fugir sem iteração através de cada personagem. Não há nenhuma maneira de saber se o personagem é minúscula ou maiúscula de outra forma.

Se você realmente odeia tolower() , aqui está uma ASCII- especializada única alternativa que eu não recomendo que você usar:

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

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

Esteja ciente de que tolower() só pode fazer uma substituição per-bytes de um único caractere, que é mal ajustadas para muitos scripts, especialmente se estiver usando um multi-byte-codificação como UTF-8.

Outras dicas

impulso fornece um algoritmo de corda para este :

#include <boost/algorithm/string.hpp>

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

Ou, para não-in- lugar :

#include <boost/algorithm/string.hpp>

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

tl; dr

Use a UTI biblioteca .


Primeiro você tem que responder a uma pergunta: Qual é a codificação do seu std::string? É ISO-8859-1? Ou talvez ISO-8859-8? Ou Windows Codepage 1252? faz tudo o que você está usando para converter upper-to-minúsculas sabe disso? (Ou faz isso falhar miseravelmente para personagens mais 0x7f?)

Se você estiver usando UTF-8 (a única escolha sã entre as codificações de 8 bits) com std::string como recipiente, você já está enganando a si mesmo em acreditar que você ainda está no controle das coisas, porque você está armazenando um caractere multibyte sequência em um recipiente que não está ciente do conceito de vários bytes. Mesmo algo tão simples como .substr() é uma bomba-relógio tique-taque. (Uma vez que a divisão de uma sequência de vários bytes vai resultar em uma cadeia inválida (sub).)

E, logo que você tentar algo como std::toupper( 'ß' ), no qualquer codificação, você está em apuros. (Porque ele simplesmente não é possível fazer isso "certo" com a biblioteca padrão, o que só pode entregar um personagem 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' seria correto; na Turquia, 'ı' (a minúsculo dotless I) é o resultado esperado (que, mais uma vez, é mais do que um byte em UTF-8).

Depois, há o ponto que a biblioteca padrão está dependendo de qual localidades são suportado na máquina o software está em execução no ... e o que fazer se não for?

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

(C ++ 11 nota:. std::u16string e std::u32string são melhor , mas ainda não é perfeito)

Enquanto impulso aparência bom, API sábio, Boost.Locale é basicamente um invólucro em torno UTI . Se Boost é compilado com o apoio UTI ... se não é, Boost.Locale se limita ao apoio local compilado para a biblioteca padrão.

E acredite em mim, recebendo impulso para compilar com UTI pode ser uma verdadeira dor às vezes. (Não há binários pré-compilados para o Windows, então você tem que fornecê-los, juntamente com a sua candidatura, e que abre uma lata nova de vermes ...)

Então, pessoalmente eu recomendaria começar completo Unicode apoio direto da boca do cavalo e usando a biblioteca UTI diretamente :

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

#include <iostream>

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

compilação (com G ++ neste exemplo):

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

Isto dá:

eidengesäß
EIDENGESÄSS

[1] Em 2017, o Conselho Alemão Orthography decidiu que "?" U + 1E9E LATIN CAPITAL CARTA SHARP S poderia ser usado oficialmente, como uma opção ao lado do tradicional "SS" conversão à ambigüidade evitar, por exemplo, nos passaportes (onde os nomes são capitalizados). Minha linda go-to exemplo, tornada obsoleta pela decisão da comissão ...

Se o texto contém caracteres UTF-8 fora da faixa de ASCII, então boost :: algoritmo :: to_lower não irá converter aqueles. Melhor utilização boost :: local :: to_lower quando UTF-8 está envolvido. Consulte http://www.boost.org/doc/libs/1_51_0 /libs/locale/doc/html/conversions.html

Usando gama baseada em loop for de C ++ 11 um código mais simples seria:

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

Este é um follow-up para a resposta de Stefan Mai: se você gostaria de colocar o resultado da conversão em outra seqüência, você precisa pré-alocar seu espaço de armazenamento antes de chamar std::transform. Desde que as lojas STL transformado caracteres no iterador de destino (incrementá-lo a cada iteração do loop), a string de destino não será redimensionada automaticamente, e corre o risco de pisar memória.

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

Outra abordagem utilizando intervalo com base loop com referência variável

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

cout<<test<<endl;

Tanto quanto eu ver bibliotecas de impulso são muito ruins em termos de performance. Eu testei sua unordered_map a STL e foi média 3 vezes mais lenta (melhor caso 2, o pior foi 10 vezes). Além disso, este algoritmo procura muito baixa.

A diferença é tão grande que eu tenho certeza que qualquer que seja disso que você precisa fazer para tolower para torná-lo igual a aumentar "para as suas necessidades" serão maneira mais rápida do que impulso.

Eu fiz esses testes em um EC2 Amazon, portanto, o desempenho variar durante o ensaio, mas você ainda é 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 feita 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 eu deveria para os testes em uma máquina dedicada, mas vou estar usando este EC2 então eu realmente não preciso testá-lo na minha máquina.

A maneira mais simples para string converter em loweercase sem se preocupar com namespace std é a seguinte

1: string com / sem espaços

#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: string sem espaços

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

std::ctype::tolower() do C ++ Localização biblioteca padrão irá corretamente fazer isso por você. Aqui está um exemplo extraído do tolower página de referência

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

Uma alternativa para Boost é POCO (pocoproject.org).

POCO oferece duas variantes:

  1. A primeira variante faz uma cópia sem alterar a string original.
  2. A segunda variante muda a string original no lugar.
    versões "in place" sempre "InPlace" no nome.

Ambas as versões são demonstrados 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 diminuir Sem fazer se os testes , e é muito simples. Use a função / de macro isupper () de clocale.h deve cuidar de problemas relacionados com a sua localização, mas se não, você sempre pode ajustar o Utol [] para o conteúdo do seu coração.

Tendo em conta que os personagens de C são realmente apenas ints de 8 bits (ignorando os amplos conjuntos de caracteres para o momento), você pode criar uma matriz de 256 bytes segurando um conjunto alternativo de caracteres e no uso função de conversão dos caracteres em sua seqüência como subscritos para a matriz de conversão.

Em vez de um mapeamento um-para-um no entanto, dar os membros da matriz maiúsculas os valores int byte para as letras minúsculas. Você pode encontrar islower () e isupper () útil aqui.

enter descrição da imagem aqui

O código fica assim ...

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

Esta abordagem, ao mesmo tempo, permitir que você remapear quaisquer outros caracteres que você deseja alterar.

Esta abordagem tem uma vantagem enorme quando rodando em processadores modernos, não há necessidade de fazer previsão de desvios como não há se testes compreendendo ramificação. Isso economiza lógica de previsão de desvio do CPU para outros loops, e tende a evitar barracas de dutos.

Alguns aqui podem reconhecer esta abordagem como o mesmo usado para converter EBCDIC para 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, nota que @ o comentário de AndreasSpindler em esta resposta ainda é uma consideração importante, no entanto, se você está trabalhando em algo que não é apenas caracteres ASCII.

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

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

Existe uma alternativa que funciona 100% do tempo?

Não

Há várias perguntas que você precisa perguntar a si mesmo antes de escolher um método lowercasing.

  1. Como é a string codificada? ASCII? UTF-8? alguma forma de codificação legado ASCII estendida?
  2. O que você quer dizer com letras minúsculas, afinal? regras de mapeamento caso variar entre as línguas! Você quer algo que está localizada na localidade de usuários? Você quer algo que se comporta de forma consistente em todos os sistemas de suas corridas de software on? Você só quer para minúsculas caracteres ASCII e passar por tudo o resto?
  3. Quais as bibliotecas estão disponíveis?

Depois de ter respostas a essas perguntas, você pode começar a procurar um soloution que satisfaça suas necessidades. Não há um tamanho único que funcione para todos em todos os lugares!

Uma vez que nenhuma das respostas mencionou a próxima biblioteca Ranges, que está disponível na biblioteca padrão desde C ++ 20, e atualmente disponível separadamente no GitHub como range-v3, eu gostaria de acrescentar uma maneira de realizar essa conversão usando-o.

Para modificar a seqüência no local:

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

Para gerar uma nova string:

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

(Não se esqueça de #include <cctype> e as escalas requeridas cabeçalhos.)

Nota: o uso de unsigned char como o argumento para o lambda é inspirado cppreference , que afirma:

Como todas as outras funções de <cctype>, o comportamento de std::tolower é indefinido se o valor do argumento não é nem representável como unsigned char nem igual a EOF. Para utilizar estas funções com segurança com chars simples (ou signed chars), o argumento deve primeiro ser convertidos para unsigned char:

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

Da mesma forma, eles não devem ser usados ??diretamente com algoritmos padrão quando o tipo de valor do iterador é char ou signed char. Em vez disso, converter o valor para unsigned char primeiro:

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

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

Fragmento de Código

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

Use fplus :: to_lower_case ().

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

Procurar 'to_lower_case' em http://www.editgym.com/fplus-api -search / )

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

Copiar porque foi anulado para melhorar resposta. Graças SO


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

Explicação:

for(auto& c : test) é uma baseada-gama para circuito do tipo
for ( range_declaration : range_expression ) loop_statement :

  1. range_declaration : auto& c
    Aqui o auto especificador é usado para para o tipo de dedução automática. Então, o tipo fica deduzidos das variáveis ??inicializador.

  2. range_expression : test
    A gama neste caso são os personagens de test string.

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

C ++ não tem tolower ou métodos ToUpper implementadas para cordas, mas está disponível para Char. Pode-se facilmente ler cada caractere de corda, convertê-lo em caso necessário e colocá-lo de volta para string. Um código de exemplo sem usar qualquer biblioteca de terceiros:

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

Para a operação baseada em caracteres na string: Para cada caracteres em cadeia

As minhas próprias funções modelo que realiza maiúsculas / minúsculas.

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

Esta poderia ser uma outra versão simples para converter maiúsculas para minúsculas e vice-versa. Eu costumava VS2017 versão comunitária para compilar este código-fonte.

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

Nota:. Se houver caracteres especiais em seguida, precisam ser tratados usando verificação de condição

Eu tentei std :: transformar, todos get i é abominável stl erro de compilação CRIPTIC que apenas druidas de 200 anos pode entender (não é possível converter a gripe flibidi flabidi)

isso funciona bem e pode ser facilmente tweaked

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;
}
//You can really just write one on the fly whenever you need one.
#include <string>
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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top