Como alguém poderia converter uma string para maiúscula? Os exemplos que encontrei no Google apenas têm que lidar com caracteres.
268
Algoritmos de cadeia de impulso:
#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
provavelmente assume ASCII.std::string newstr(boost::to_upper_copy<std::string>("Hello World"));
fonte
toupper()
pode ser implementado como uma macro. Isso pode causar um problema.toupper
. Alguma ideia?Solução curta usando C ++ 11 e toupper ().
fonte
c
seria doconst char
tipo (deauto
)? Nesse caso, você não pode atribuí-lo (por causa daconst
parte) ao que é retornadotoupper(c)
.c
precisaunsigned char
ser usado para que isso seja corrigido.Nota: Alguns problemas com a solução superior:
O que significa que os
cctype
membros podem muito bem ser macros não adequadas para consumo direto em algoritmos padrão.Outro problema com o mesmo exemplo é que ele não lança o argumento ou verifica se isso não é negativo; isso é especialmente perigoso para sistemas em que a planície
char
é assinada. (A razão é: se isso for implementado como uma macro, provavelmente usará uma tabela de pesquisa e seus argumentos serão indexados nessa tabela. Um índice negativo fornecerá UB.)fonte
Esse problema é vetorizável com SIMD para o conjunto de caracteres ASCII.
Comparações de aceleração:
Teste preliminar com x86-64 gcc 5.2
-O3 -march=native
em um Core2Duo (Merom). A mesma sequência de 120 caracteres (ASCII misturada em minúsculas e não minúsculas), convertida em um loop 40M vezes (sem inlining de arquivos cruzados, para que o compilador não possa otimizar ou retirar nada disso do loop). Os mesmos buffers de origem e de destino, portanto, sem sobrecarga de malloc ou efeitos de memória / cache: os dados ficam quentes no cache L1 o tempo todo e somos puramente vinculados à CPU.boost::to_upper_copy<char*, std::string>()
: 198.0s . Sim, o Boost 1.58 no Ubuntu 15.10 é realmente muito lento. Eu criei um perfil e dei um único passo no asm em um depurador, e é muito, muito ruim: há um broadcast_ dinâmico de uma variável de localidade acontecendo por caractere !!! (dynamic_cast recebe várias chamadas para strcmp). Isso acontece comLANG=C
e comLANG=en_CA.UTF-8
.Eu não testei usando um RangeT diferente de std :: string. Talvez a outra forma de
to_upper_copy
otimiza melhor, mas eu acho que vai semprenew
/malloc
espaço para a cópia, por isso é mais difícil de teste. Talvez algo que eu fiz seja diferente de um caso de uso normal, e talvez o g ++ normalmente interrompido possa elevar o material de configuração do código de idioma do loop por caractere. Meu loop lendo destd::string
e escrevendo para achar dstbuf[4096]
faz sentido para o teste.loop chamando glibc
toupper
: 6.67s ( embora não verifique oint
resultado para um potencial UTF-8 de vários bytes. Isso importa para o turco.)cmov
, com a tabela quente em L1 de qualquer maneira.Veja também esta pergunta sobre
toupper()
ser lento no Windows quando um código de idioma estiver definido .Fiquei chocado que Boost é uma ordem de magnitude mais lenta que as outras opções. Verifiquei duas vezes se havia
-O3
ativado e até dei um passo para ver o que estava fazendo. É quase exatamente a mesma velocidade com o clang ++ 3.8. Possui uma sobrecarga enorme dentro do loop por caractere. O resultadoperf record
/report
(para ocycles
evento perf) é:Autovectorização
Gcc e clang apenas auto-vectorizam loops quando a contagem de iterações é conhecida antes do loop. (ou seja, os loops de pesquisa, como a implementação em C simples
strlen
, não se autovectorizam.)Portanto, para strings pequenas o suficiente para caber no cache, obtemos uma aceleração significativa para strings com ~ 128 caracteres desde o
strlen
início. Isso não será necessário para cadeias de comprimento explícito (como C ++std::string
).Qualquer libc decente terá uma eficiência
strlen
que é muito mais rápido do que repetir um byte de cada vez; portanto, loops vetorizados de strlen e toupper separados são mais rápidos.Linha de base: um loop que verifica um 0 de terminação em tempo real.
Tempos para iterações de 40M, em um Core2 (Merom) 2.4GHz. gcc 5.2
-O3 -march=native
. (Ubuntu 15.10).dst != src
(por isso fazemos uma cópia), mas eles não se sobrepõem (e não estão por perto). Ambos estão alinhados.Alguns resultados são um pouco diferentes com o clang.
O loop de marca de microbench que chama a função está em um arquivo separado. Caso contrário, ele alinha e
strlen()
é içado para fora do loop, e corre drasticamente mais rápido, esp. para 16 strings de caracteres (0,187s).Isso tem a grande vantagem de que o gcc pode auto-vetorizá-lo para qualquer arquitetura, mas a grande desvantagem de ser mais lento no caso geralmente comum de pequenas seqüências de caracteres.
Portanto, existem grandes acelerações, mas a vetorização automática do compilador não produz um ótimo código, esp. para limpeza dos últimos até 15 caracteres.
Vetorização manual com intrínsecas SSE:
Baseado na minha função flip-case que inverte o caso de todos os caracteres alfabéticos. Ele tira proveito do "truque de comparação não assinado", onde você pode fazer
low < a && a <= high
uma única comparação não assinada por mudança de intervalo, para que qualquer valor menor que sejalow
agrupado em um valor maior quehigh
. (Isso funciona selow
ehigh
não estiver muito distante.)O SSE possui apenas uma comparação maior assinada, mas ainda podemos usar o truque "comparação não assinada" deslocando o intervalo para o final do intervalo assinado: Subtraia 'a' + 128, para que os caracteres alfabéticos variem de -128 a -128 +25 (-128 + 'z' - 'a')
Observe que adicionar 128 e subtrair 128 são a mesma coisa para números inteiros de 8 bits. Não há nenhum lugar para o transporte ir, por isso é apenas xor (add carryless), invertendo a parte mais alta.
Dada essa função que funciona para um vetor, podemos chamá-la em um loop para processar uma string inteira. Como já estamos segmentando o SSE2, podemos fazer uma verificação de fim de cadeia vetorizada ao mesmo tempo.
Também podemos fazer muito melhor para a "limpeza" dos últimos até 15 bytes restantes após a execução de vetores de 16B: a caixa superior é idempotente, portanto, reprocessar alguns bytes de entrada é bom. Fazemos uma carga desalinhada dos últimos 16B da fonte e a armazenamos no buffer de destino sobrepondo a última loja 16B do loop.
O único momento em que isso não funciona é quando toda a cadeia está abaixo de 16B: Mesmo quando
dst=src
a leitura-modificação-gravação não atômica não é a mesma coisa que não tocar em alguns bytes e pode quebrar o código multithread.Temos um loop escalar para isso e também para nos
src
alinharmos. Como não sabemos onde estará o 0 final, uma carga desalinhada poderá passarsrc
para a próxima página e ocorrer um defeito. Se precisarmos de bytes em um pedaço 16B alinhado, é sempre seguro carregar todo o pedaço 16B alinhado.Fonte completa: em uma essência do github .
Tempos para iterações de 40M, em um Core2 (Merom) 2.4GHz. gcc 5.2
-O3 -march=native
. (Ubuntu 15.10).dst != src
(por isso fazemos uma cópia), mas eles não se sobrepõem (e não estão por perto). Ambos estão alinhados.(Na verdade, com o tempo programado
_mm_store
no loop, não_mm_storeu
, porque o storeu é mais lento no Merom, mesmo quando o endereço está alinhado. É bom no Nehalem e mais tarde. Eu também deixei o código como está por enquanto, em vez de corrigir a falha de cópia o 0 final em alguns casos, porque não quero cronometrar tudo.)Portanto, para cadeias curtas maiores que 16B, isso é drasticamente mais rápido que vetorizado automaticamente. Comprimentos com largura inferior a um vetor não representam um problema. Eles podem ser um problema ao operar no local, devido a um estol de encaminhamento de loja. (Mas observe que ainda é bom processar nossa própria saída, em vez da entrada original, porque o toupper é idempotente).
Há muito espaço para ajustar isso para diferentes casos de uso, dependendo do que o código circundante deseja e da microarquitetura de destino. Conseguir que o compilador emita um código legal para a parte da limpeza é complicado. Usar
ffs(3)
(que compila para bsf ou tzcnt no x86) parece bom, mas obviamente esse bit precisa ser repensado, pois notei um bug depois de escrever a maior parte dessa resposta (consulte os comentários do FIXME).Acelerações de vetor para seqüências ainda menores podem ser obtidas com
movq
oumovd
cargas / armazenamentos. Personalize conforme necessário para o seu caso de uso.UTF-8:
Podemos detectar quando nosso vetor possui bytes com o conjunto de bits alto e, nesse caso, retornar a um loop escalar com reconhecimento utf-8 para esse vetor. O
dst
ponto pode avançar em uma quantidade diferente dosrc
ponteiro, mas quando voltarmos a umsrc
ponteiro alinhado , ainda faremos armazenamentos de vetor desalinhadosdst
.Para texto que é UTF-8, mas consiste principalmente do subconjunto ASCII de UTF-8, isso pode ser bom: alto desempenho no caso comum, com comportamento correto em todos os casos. Porém, quando há muito não ASCII, provavelmente será pior do que permanecer no loop ciente UTF-8 escalar o tempo todo.
Tornar o inglês mais rápido às custas de outros idiomas não é uma decisão preparada para o futuro se a desvantagem for significativa.
Reconhecimento de localidade:
No código do idioma turco (
tr_TR
), o resultado corretotoupper('i')
é'İ'
(U0130), não'I'
(ASCII simples). Veja os comentários de Martin Bonner sobre uma pergunta sobretolower()
ser lento no Windows.Também podemos verificar se há uma lista de exceções e um fallback para escalar, como caracteres de entrada UTF8 de vários bytes.
Com essa complexidade, o SSE4.2
PCMPISTRM
ou algo assim pode ser capaz de realizar muitas de nossas verificações de uma só vez.fonte
Você tem caracteres ASCII ou internacionais em strings?
Se for o último caso, "maiúsculas" não é tão simples e depende do alfabeto usado. Existem alfabetos bicameral e unicameral. Somente alfabetos bicameral têm caracteres diferentes para maiúsculas e minúsculas. Além disso, existem caracteres compostos, como a letra maiúscula em latim 'DZ' (\ u01F1 'DZ') que usam a chamada maiúscula . Isso significa que apenas o primeiro caractere (D) é alterado.
Eu sugiro que você analise a UTI e a diferença entre mapeamentos de casos simples e completos. Isso pode ajudar:
http://userguide.icu-project.org/transforms/casemappings
fonte
Ou,
fonte
**
após os parâmetros na primeira solução fazem?**
é um erro de digitação que resta de tentar usar fonte em negrito na sintaxe do código.toupper
é chamado com números negativos.O seguinte funciona para mim.
fonte
toupper
é chamado com números negativos.Use uma lambda.
fonte
O mais rápido se você usar apenas caracteres ASCII :
Observe que esse código é executado mais rapidamente, mas funciona apenas em ASCII e não é uma solução "abstrata".
Se você precisar de soluções UNICODE ou soluções mais convencionais e abstratas, procure outras respostas e trabalhe com métodos de seqüências de caracteres C ++.
fonte
C++
, mas você escreveu umaC
resposta aqui. (Eu não sou um dos downvoters.)'
?Desde que você esteja bem com apenas ASCII e possa fornecer um ponteiro válido para a memória RW, existe um one-liner simples e muito eficaz em C:
Isso é especialmente bom para cadeias simples, como identificadores ASCII, que você deseja normalizar no mesmo caso de caractere. Você pode usar o buffer para construir uma instância std: string.
fonte
fonte
for (size_t i = 0 ...
. Também não há boas razões para dificultar a leitura. Isso também copia a string primeiro e depois passa sobre ela. @ A resposta de Luke é melhor em alguns aspectos, exceto por não tirar vantagem das'a'
constantes de caracteres.Isso terá um desempenho melhor do que todas as respostas que usam a função global toupper e é presumivelmente o que boost :: to_upper está fazendo por baixo.
Isso ocorre porque :: toupper precisa procurar a localidade - porque pode ter sido alterada por um encadeamento diferente - para cada chamada, enquanto aqui apenas a chamada para locale () tem essa penalidade. E procurar o código do idioma geralmente envolve trancar o cadeado.
Isso também funciona com o C ++ 98 após a substituição automática, o uso do novo non-const str.data () e a adição de um espaço para interromper o fechamento do modelo (">>" para ">>") assim:
fonte
fonte
reserve
eback_inserter
(fazendo com que a sequência seja copiada apenas uma vez).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; }
fonte
toupper
é chamado com números negativos.tente o
toupper()
função (#include <ctype.h>
). aceita caracteres como argumentos, as strings são compostas de caracteres, portanto, você terá que iterar sobre cada caractere individual que, quando reunidos, compreende a stringfonte
toupper
é chamada com números negativos. Você deveria ter mencionado o elenco necessáriounsigned char
.Aqui está o código mais recente com o C ++ 11
fonte
toupper
é chamado com números negativos.Usando Boost.Text, que funcionará para texto Unicode
fonte
A resposta de @dirkgently é muito inspiradora, mas quero enfatizar que, devido à preocupação mostrada abaixo,
o uso correto de
std::toupper
deve ser:Resultado:
fonte
não tenho certeza se existe uma função incorporada. Tente o seguinte:
Inclua as bibliotecas ctype.h OR cctype, bem como o stdlib.h como parte das diretivas do pré-processador.
fonte
toupper
é chamado com números negativos.Minha solução (limpando o sexto bit para alfa):
fonte
toupper
é chamado com números negativos.Todas essas soluções nesta página são mais difíceis do que precisam.
Faça isso
RegName
é seustring
. Obtenha seu tamanho de string, não usestring.size()
como seu testador real, muito confuso e pode causar problemas. então. ofor
loop mais básico .Lembre-se de que o tamanho da string também retorna o delimitador, portanto, use <e não <= no seu teste de loop.
a saída será: alguma string que você deseja converter
fonte
tolower
loops simples , e a maioria deles usa nomes de variáveis de loop padrãoi
, como não estranhosforLoop
.Sem usar nenhuma biblioteca:
fonte
Se você estiver preocupado apenas com caracteres de 8 bits (que todas as outras respostas, exceto Milan Babuškov também assumem), poderá obter a velocidade mais rápida gerando uma tabela de consulta em tempo de compilação usando metaprogramação. No ideone.com, isso é executado 7 vezes mais rápido que a função de biblioteca e 3x mais rápido que uma versão escrita à mão ( http://ideone.com/sb1Rup ). Também é personalizável através de características sem desaceleração.
com caso de uso:
Para uma descrição detalhada (em várias páginas) de como ele funciona, permita-me conectar descaradamente o meu blog: http://metaporky.blogspot.de/2014/07/part-4-generating-look-up-tables-at.html
fonte
fonte
Essa função c ++ sempre retorna a cadeia de caracteres em maiúsculas ...
fonte
Eu uso essa solução. Eu sei que você não deve modificar essa área de dados .... mas acho que isso é principalmente para erros de saturação de buffer e caracteres nulos ... as coisas de maiúsculas e minúsculas não são a mesma coisa.
fonte
I know you're not supposed to modify that data area
- qual área de dados você não deve modificar?str[i] = toupper(str[i]);
perfeitamente fina (bem, não perfeitamente fina, mas corrige a maioria das coisas erradas).