Quero converter um std::string
para minúsculo. Estou ciente da função tolower()
, no entanto, no passado, tive problemas com essa função e ela dificilmente é ideal, pois o uso com a std::string
exigiria a iteração sobre cada caractere.
Existe uma alternativa que funcione 100% do tempo?
c++
string
c++-standard-library
tolower
Konrad
fonte
fonte
Respostas:
Adaptado de perguntas não tão frequentes :
Você realmente não vai se safar sem percorrer cada personagem. Não há como saber se o personagem é minúsculo ou maiúsculo.
Se você realmente odeia
tolower()
, aqui está uma alternativa especializada apenas em ASCII que eu não recomendo que você use:Esteja ciente de que
tolower()
só é possível fazer uma substituição por caractere de byte único, o que é inadequado para muitos scripts, especialmente se você estiver usando uma codificação de bytes múltiplos como UTF-8.fonte
char
para::tolower(int)
.) Você precisa garantir que não passa um valor negativo.::tolower
pode travar, é UB para entrada não ASCII.O Boost fornece um algoritmo de string para isso :
Ou, para não local :
fonte
to_lower_copy
tl; dr
Use a biblioteca da UTI . Caso contrário, sua rotina de conversão será interrompida silenciosamente nos casos que você provavelmente nem conhece.
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 página de código 1252 do Windows? O que você está usando para converter maiúsculas para minúsculas sabe disso? (Ou falha miseravelmente para os personagens terminados0x7f
?)Se você estiver usando UTF-8 (a única opção sensata entre as codificações de 8 bits)
std::string
como contêiner, já estará enganando-se a acreditar que ainda está no controle das coisas, porque está armazenando uma sequência de caracteres multibyte em um contêiner que não conhece o conceito multibyte. Mesmo algo tão simples quanto.substr()
uma bomba-relógio. (Como a divisão de uma sequência multibyte resultará em uma (sub)) string inválida.)E assim que você tenta algo como
std::toupper( 'ß' )
, em qualquer codificação, está com problemas profundos. (Como simplesmente não é possível fazer isso "corretamente" com a biblioteca padrão, que pode fornecer apenas um caractere de resultado, não o"SS"
necessário aqui.) [1] Outro exemplo seriastd::tolower( 'I' )
: o que deve gerar resultados diferentes, dependendo da localidade . Na Alemanha,'i'
estaria correto; na Turquia,'ı'
(LETRA LATINA PEQUENA I) é o resultado esperado (que, novamente, é mais de um byte na codificação UTF-8). Ainda outro exemplo é o sigma grego , maiúsculo'∑'
e minúsculo'σ'
... exceto no final de uma palavra, onde está'ς'
.Portanto, qualquer conversão de caso que funcione em um caractere de cada vez, ou pior, em um byte de cada vez, é interrompida pelo design.
Depois, há o ponto de que a biblioteca padrão, para o que é capaz de fazer, depende de quais localidades são suportadas na máquina em que seu software está executando ... e o que você faz se não estiver?
Então, o que você realmente está procurando é uma classe de string capaz de lidar com tudo isso corretamente, e essa não é nenhuma das
std::basic_string<>
variantes .(Nota do C ++ 11:
std::u16string
estd::u32string
são melhores , mas ainda não são perfeitas. O C ++ 20 trouxestd::u8string
, mas tudo o que faz é especificar a codificação. Em muitos outros aspectos, eles ainda permanecem ignorantes da mecânica do Unicode, como normalização, agrupamento, etc. .)Enquanto o Boost parece bom, em termos de API, o Boost.Locale é basicamente um invólucro em torno da ICU . Se o Boost for compilado com suporte à ICU ... se não for, o Boost.Locale será limitado ao suporte de localidade compilado para a biblioteca padrão.
E acredite em mim, fazer com que o Boost compile com a UTI pode ser uma dor real às vezes. (Como não existem binários pré-compilados para o Windows, você precisará fornecê-los juntamente com o 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 a biblioteca da ICU diretamente:
Compile (com G ++ neste exemplo):
Isto dá:
Observe que a conversão Σ <-> σ no meio da palavra e a conversão Σ <-> ς no final da palavra. Uma
<algorithm>
solução não baseada pode lhe dar isso.[1] Em 2017, o Conselho de Ortografia Alemã determinou que "ẞ" U + 1E9E LETRA DE CAPITAL LATINA SHARP S poderia ser usada oficialmente, como uma opção ao lado da conversão tradicional de "SS" para evitar ambiguidade, por exemplo, em passaportes (onde os nomes são maiúsculos ) Meu lindo exemplo, tornado obsoleto por decisão do comitê ...
fonte
toupper
etolower
ainda funcionam em caracteres únicos. A classe de string ainda não tem noção de normalização (por exemplo, se um "ü" é codificado como "u com diaeresis" ou "u + diaeresis combinada") ou onde uma string pode ou não ser separada. A lista continua. u8string é (como as outras classes de string padrão) apropriadas para "passagem". Mas se você deseja processar Unicode, precisa de uma UTI.Usando o loop for baseado em intervalo do C ++ 11, um código mais simples seria:
fonte
Se a sequência contiver caracteres UTF-8 fora do intervalo ASCII, o boost :: algoritmo :: to_lower não os converterá. Melhor usar boost :: locale :: to_lower quando UTF-8 estiver envolvido. Consulte http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/conversions.html
fonte
Este é um acompanhamento da resposta de Stefan Mai: se você deseja colocar o resultado da conversão em outra string, é necessário pré-alocar seu espaço de armazenamento antes da chamada
std::transform
. Como o STL armazena caracteres transformados no iterador de destino (incrementando-o a cada iteração do loop), a sequência de destino não será redimensionada automaticamente e você corre o risco de perder memória.fonte
Outra abordagem usando intervalo baseado em loop com variável de referência
fonte
Até onde eu vejo, as bibliotecas Boost são realmente ruins em termos de desempenho. Testei o unordered_map para STL e a média foi três vezes mais lenta (o melhor caso 2, o pior foi 10 vezes). Além disso, esse algoritmo parece muito baixo.
A diferença é tão grande que tenho certeza de que qualquer acréscimo que você precise fazer
tolower
para torná-lo igual ao impulso "para suas necessidades" será muito mais rápido que o impulso.Fiz esses testes em um Amazon EC2; portanto, o desempenho variou durante o teste, mas você ainda entende.
-O2
fez assim:Fonte:
Acho que devo fazer os testes em uma máquina dedicada, mas usarei este EC2 para que eu realmente não precise testá-lo na minha máquina.
fonte
A maneira mais simples de converter string em loweercase sem se preocupar com o namespace std é a seguinte
1: string com / sem espaços
2: string sem espaços
fonte
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 para baixarfonte
const
? Isso parece torná-lo um pouco mais confuso (por exemplo, não parece que você possa usarf.tolower()
), já que você precisa colocar os caracteres em uma nova string. Você usariatransform()
e algo parecidostd::bind1st( std::mem_fun() )
com o operador?tolower
comlocale
parâmetro, a chamada implícita parause_facet
parece ser um gargalo de desempenho. Um dos meus colegas de trabalho alcançou um aumento de velocidade de vários 100% ao substituirboost::iequals
(que tem esse problema) por uma versão em queuse_facet
é chamada apenas uma vez fora do loop.Uma alternativa ao Boost é o POCO (pocoproject.org).
O POCO fornece duas variantes:
As versões "no local" sempre têm "InPlace" no nome.
Ambas as versões são demonstradas abaixo:
fonte
Existe uma maneira de converter maiúsculas para minúsculas SEM fazer testes , e é bem direto. O uso da função isupper () / macro do clocale.h deve resolver os problemas relacionados à sua localização, mas, se não, você sempre pode ajustar o UtoL [] ao conteúdo do seu coração.
Dado que os caracteres de C são realmente apenas ints de 8 bits (ignorando os amplos conjuntos de caracteres no momento), você pode criar uma matriz de 256 bytes contendo um conjunto alternativo de caracteres e, na função de conversão, use os caracteres da 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 int BYTE para os caracteres minúsculos. Você pode achar islower () e isupper () útil aqui.
O código fica assim ...
Essa abordagem permitirá, ao mesmo tempo, que você remapeie os outros caracteres que deseja alterar.
Essa abordagem tem uma enorme vantagem ao executar em processadores modernos, não há necessidade de fazer previsão de ramificação, pois não há testes que incluam ramificação. Isso economiza a lógica de previsão de ramificação da CPU para outros loops e tende a evitar paralisações no pipeline.
Alguns aqui podem reconhecer essa abordagem como a mesma usada para converter EBCDIC em ASCII.
fonte
Como 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
, gostaria de adicionar uma maneira de executar essa conversão usando-a.Para modificar a sequência no local:
Para gerar uma nova sequência:
(Não esqueça
#include <cctype>
dos cabeçalhos de intervalos necessários.)Nota: o uso de
unsigned char
como argumento para o lambda é inspirado na cppreference , que afirma:fonte
Meu próprio modelo funciona, que executa maiúsculas / minúsculas.
fonte
towlower
caracteres largos que suportam o UTF-16.Aqui está uma técnica de macro, se você quiser algo simples:
No entanto, observe que o comentário de @ AndreasSpindler sobre esta resposta ainda é uma consideração importante, no entanto, se você estiver trabalhando em algo que não é apenas caracteres ASCII.
fonte
void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
x
pode ser uma expressão válida, que apenas compila corretamente, mas fornece resultados completamente falsos por causa das macros.Para mais informações: http://www.cplusplus.com/reference/locale/tolower/
fonte
Não
Há várias perguntas que você precisa se perguntar antes de escolher um método em minúsculas.
Depois de ter respostas para essas perguntas, você pode começar a procurar uma solução que atenda às suas necessidades. Não existe um tamanho único que funcione para todos em todos os lugares!
fonte
Tente esta função :)
fonte
Nas plataformas da Microsoft, você pode usar a
strlwr
família de funções: http://msdn.microsoft.com/en-us/library/hkxwh33z.aspxfonte
Fragmento de código
fonte
Use fplus :: to_lower_case ().
(fplus: https://github.com/Dobiasd/FunctionalPlus .
Pesquise 'to_lower_case' em http://www.editgym.com/fplus-api-search/ )
fonte
Copie porque não foi permitido melhorar a resposta. Obrigado SO
Explicação:
for(auto& c : test)
é um loop for baseado em intervalo do tipo :for (
range_declaration
:
range_expression
)
loop_statement
range_declaration
:auto& c
Aqui o especificador automático é usado para dedução automática de tipo. Portanto, o tipo é deduzido do inicializador de variáveis.
range_expression
:test
O intervalo, neste caso, são os caracteres da string
test
.Os caracteres da sequência
test
estão disponíveis como uma referência dentro do identificador de loop forc
.fonte
O C ++ não possui métodos tolower ou toupper implementados para string, mas está disponível para char. Pode-se facilmente ler cada caractere de string, convertê-lo no caso necessário e colocá-lo novamente em string. Um código de amostra sem usar nenhuma biblioteca de terceiros:
Para operação baseada em caracteres na sequência: Para cada caractere na sequência
fonte
Esta poderia ser outra versão simples para converter maiúsculas em minúsculas e vice-versa. Usei a versão da comunidade VS2017 para compilar esse código fonte.
Nota: se houver caracteres especiais, precisará ser tratado usando a verificação de condição.
fonte
Eu tentei std :: transform, tudo o que recebo é um abominável erro de compilação stl criptic que somente druidas de 200 anos atrás podem entender (não é possível converter de para flibidi flabidi flu)
isso funciona bem e pode ser facilmente ajustado
fonte