Converter uma seqüência de caracteres em C ++ em maiúsculas


268

Como alguém poderia converter uma string para maiúscula? Os exemplos que encontrei no Google apenas têm que lidar com caracteres.

Respostas:


205

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

5
Isso também tem o benefício do i18n, onde ::toupperprovavelmente assume ASCII.
Ben Straub

4
Sua última linha não compilar - você tem que mudar para algo como:std::string newstr(boost::to_upper_copy<std::string>("Hello World"));
maxschlepzig

58
essa não deve ser a resposta aceita, pois exige reforço ou o título deve ser alterado.
Andrea

44
sim, vou instalar o boost apenas para to_upper ... excelente ideia! </sarcasm> :)
thang 31/05

12
Pessoalmente, estou menos disposto a impulsionar como resposta a "como faço x em C ++?" porque o impulso não é uma solução leve. Parece que você compra o boost como uma estrutura (ou ACE ou Qt ou Recusion ToolKit ++ ou ...) ou não. Eu preferiria ver soluções de linguagem.
jwm

486
#include <algorithm>
#include <string>

std::string str = "Hello World";
std::transform(str.begin(), str.end(),str.begin(), ::toupper);

8
Na verdade, toupper()pode ser implementado como uma macro. Isso pode causar um problema.
dirkgently

3
um bind (:: toupper, construa <char não assinado> (_ 1)) com boost.lambda servirá perfeitamente bem, eu acho.
Johannes Schaub - litb

11
Essa abordagem funciona bem para ASCII, mas falha em codificações de caracteres de vários bytes ou em regras especiais de caixa, como o alemão 'ß'.
dan04

9
Alterei a resposta aceita para a que utilizava as bibliotecas de reforço, porque era mais rápida (nos meus testes informais), mais fácil de usar e não tinha os problemas associados a esta solução. Ainda é uma boa solução para casos em que o impulso não pode ser usado.
precisa saber é o seguinte

2
Não consigo entender por que o compilador rejeita esse código sem o :: qualifier antes toupper. Alguma ideia?
Sasha.sochka

89

Solução curta usando C ++ 11 e toupper ().

for (auto & c: str) c = toupper(c);

Não cseria do const chartipo (de auto)? Nesse caso, você não pode atribuí-lo (por causa da constparte) ao que é retornado toupper(c).
PolGraphic

5
@PolGraphic: Range - based para usa os métodos begin () / end () do contêiner para iterar sobre seu conteúdo. std :: basic_string possui uma const e um iterador mutável (retornado por cbegin () e begin () respectivamente, consulte std :: basic_string :: begin ); portanto, para (:) usa o apropriado (cbegin () se str for const declarado, com auto =: = const char, begin () caso contrário, com auto =: = char).
Thanasis Papoutsidakis

5
Veja o anser de dirkgently abaixo, cprecisa unsigned charser usado para que isso seja corrigido.
Cris Luengo

O to_upper () do boost parece muito mais consistente com as funções c ++ STL do que o toupper.
tartaruga_casco_mole 21/02

29
struct convert {
   void operator()(char& c) { c = toupper((unsigned char)c); }
};

// ... 
string uc_str;
for_each(uc_str.begin(), uc_str.end(), convert());

Nota: Alguns problemas com a solução superior:

21.5 Utilitários de sequência terminada por nulo

O conteúdo desses cabeçalhos deve ser o mesmo que os cabeçalhos da Biblioteca C Padrão <ctype.h>, <wctype.h>, <string.h>, <wchar.h> e <stdlib.h> [...]

  • O que significa que os cctypemembros 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.)


Os membros cctype normais são macros. Lembro-me de ler que eles também tinham que ser funções, embora eu não tenha uma cópia do padrão C90 e não saiba se foi explicitamente declarado ou não.
9789 David Thornley

1
eles precisam ser funções em C ++ - mesmo que C permita que sejam macros. Eu concordo com o seu segundo ponto sobre o elenco embora. a solução superior pode passar valores negativos e causar UB com isso. essa é a razão que eu não votei-lo (mas eu não votar para baixo também) :)
Johannes Schaub - litb

1
não deve faltar uma cotação padrão: 7.4.2.2/1 (litb ruim, que faz referência apenas a um rascunho C99 TC2) e C ++ 17.4.1.2/6 no padrão glory c ++ 98.
Johannes Schaub - litb

1
(observe a nota de rodapé: "Isso não permite a prática comum de fornecer uma macro de mascaramento .... blah blupp .. a única maneira de fazer isso em C ++ é fornecer uma função interna de linha externa.") :)
Johannes Schaub #

1
... que é conseguido com este truque: stackoverflow.com/questions/650461/…
Johannes Schaub - litb

27

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=nativeem 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 com LANG=Ce comLANG=en_CA.UTF-8 .

    Eu não testei usando um RangeT diferente de std :: string. Talvez a outra forma deto_upper_copy otimiza melhor, mas eu acho que vai sempre new/ mallocespaç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 de std::stringe escrevendo para a char dstbuf[4096]faz sentido para o teste.

  • loop chamando glibc toupper : 6.67s ( embora não verifique o intresultado para um potencial UTF-8 de vários bytes. Isso importa para o turco.)

  • Loop apenas ASCII: 8.79s (minha versão de linha de base para os resultados abaixo). Aparentemente, uma pesquisa de tabela é mais rápida que umacmov , com a tabela quente em L1 de qualquer maneira.
  • Auto-vetorizado apenas ASCII: 2.51s . (120 caracteres está a meio caminho entre o pior e o melhor caso, veja abaixo)
  • Somente ASCII vetorizado manualmente: 1,35s

Veja também esta pergunta sobretoupper() 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 -O3ativado 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 resultado perf record/ report(para o cyclesevento perf) é:

  32.87%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZNK10__cxxabiv121__vmi_class_type_info12__do_dyncastElNS_17__class_type_info10__sub_kindEPKS1_PKvS4_S6_RNS1_16
  21.90%  flipcase-clang-  libstdc++.so.6.0.21   [.] __dynamic_cast                                                                                                 
  16.06%  flipcase-clang-  libc-2.21.so          [.] __GI___strcmp_ssse3                                                                                            
   8.16%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZSt9use_facetISt5ctypeIcEERKT_RKSt6locale                                                                     
   7.84%  flipcase-clang-  flipcase-clang-boost  [.] _Z16strtoupper_boostPcRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE                                   
   2.20%  flipcase-clang-  libstdc++.so.6.0.21   [.] strcmp@plt                                                                                                     
   2.15%  flipcase-clang-  libstdc++.so.6.0.21   [.] __dynamic_cast@plt                                                                                             
   2.14%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZNKSt6locale2id5_M_idEv                                                                                       
   2.11%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZNKSt6locale2id5_M_idEv@plt                                                                                   
   2.08%  flipcase-clang-  libstdc++.so.6.0.21   [.] _ZNKSt5ctypeIcE10do_toupperEc                                                                                  
   2.03%  flipcase-clang-  flipcase-clang-boost  [.] _ZSt9use_facetISt5ctypeIcEERKT_RKSt6locale@plt                                                                 
   0.08% ...

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 strleninício. Isso não será necessário para cadeias de comprimento explícito (como C ++ std::string).

// char, not int, is essential: otherwise gcc unpacks to vectors of int!  Huge slowdown.
char ascii_toupper_char(char c) {
    return ('a' <= c && c <= 'z') ? c^0x20 : c;    // ^ autovectorizes to PXOR: runs on more ports than paddb
}

// gcc can only auto-vectorize loops when the number of iterations is known before the first iteration.  strlen gives us that
size_t strtoupper_autovec(char *dst, const char *src) {
    size_t len = strlen(src);
    for (size_t i=0 ; i<len ; ++i) {
        dst[i] = ascii_toupper_char(src[i]);  // gcc does the vector range check with psubusb / pcmpeqb instead of pcmpgtb
    }
    return len;
}

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.

  • String de 15 caracteres: linha de base: 1.08s. autovec: 1.34s
  • String de 16 caracteres: linha de base: 1.16s. autovec: 1.52s
  • 127 string de caracteres: linha de base: 8.91s. autovec: 2.98s // a limpeza não vetorial tem 15 caracteres para processar
  • Cadeia de caracteres de 128 caracteres: linha de base: 9.00s. autovec: 2.06s
  • Sequência de caracteres de 129 caracteres: linha de base: 9.04s. autovec: 2.07s // a limpeza não vetorial tem 1 caractere para processar

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 estrlen() é 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 <= highuma única comparação não assinada por mudança de intervalo, para que qualquer valor menor que seja lowagrupado em um valor maior que high. (Isso funciona se lowe highnã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.

#include <immintrin.h>

__m128i upcase_si128(__m128i src) {
    // The above 2 paragraphs were comments here
    __m128i rangeshift = _mm_sub_epi8(src, _mm_set1_epi8('a'+128));
    __m128i nomodify   = _mm_cmpgt_epi8(rangeshift, _mm_set1_epi8(-128 + 25));  // 0:lower case   -1:anything else (upper case or non-alphabetic).  25 = 'z' - 'a'

    __m128i flip  = _mm_andnot_si128(nomodify, _mm_set1_epi8(0x20));            // 0x20:lcase    0:non-lcase

    // just mask the XOR-mask so elements are XORed with 0 instead of 0x20
    return          _mm_xor_si128(src, flip);
    // it's easier to xor with 0x20 or 0 than to AND with ~0x20 or 0xFF
}

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=srca 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 srcalinharmos. Como não sabemos onde estará o 0 final, uma carga desalinhada poderá passar srcpara 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 .

// FIXME: doesn't always copy the terminating 0.
// microbenchmarks are for this version of the code (with _mm_store in the loop, instead of storeu, for Merom).
size_t strtoupper_sse2(char *dst, const char *src_begin) {
    const char *src = src_begin;
    // scalar until the src pointer is aligned
    while ( (0xf & (uintptr_t)src) && *src ) {
        *(dst++) = ascii_toupper(*(src++));
    }

    if (!*src)
        return src - src_begin;

    // current position (p) is now 16B-aligned, and we're not at the end
    int zero_positions;
    do {
        __m128i sv = _mm_load_si128( (const __m128i*)src );
        // TODO: SSE4.2 PCMPISTRI or PCMPISTRM version to combine the lower-case and '\0' detection?

        __m128i nullcheck = _mm_cmpeq_epi8(_mm_setzero_si128(), sv);
        zero_positions = _mm_movemask_epi8(nullcheck);
        // TODO: unroll so the null-byte check takes less overhead
        if (zero_positions)
            break;

        __m128i upcased = upcase_si128(sv);   // doing this before the loop break lets gcc realize that the constants are still in registers for the unaligned cleanup version.  But it leads to more wasted insns in the early-out case

        _mm_storeu_si128((__m128i*)dst, upcased);
        //_mm_store_si128((__m128i*)dst, upcased);  // for testing on CPUs where storeu is slow
        src += 16;
        dst += 16;
    } while(1);

    // handle the last few bytes.  Options: scalar loop, masked store, or unaligned 16B.
    // rewriting some bytes beyond the end of the string would be easy,
    // but doing a non-atomic read-modify-write outside of the string is not safe.
    // Upcasing is idempotent, so unaligned potentially-overlapping is a good option.

    unsigned int cleanup_bytes = ffs(zero_positions) - 1;  // excluding the trailing null
    const char* last_byte = src + cleanup_bytes;  // points at the terminating '\0'

    // FIXME: copy the terminating 0 when we end at an aligned vector boundary
    // optionally special-case cleanup_bytes == 15: final aligned vector can be used.
    if (cleanup_bytes > 0) {
        if (last_byte - src_begin >= 16) {
            // if src==dest, this load overlaps with the last store:  store-forwarding stall.  Hopefully OOO execution hides it
            __m128i sv = _mm_loadu_si128( (const __m128i*)(last_byte-15) ); // includes the \0
            _mm_storeu_si128((__m128i*)(dst + cleanup_bytes - 15), upcase_si128(sv));
        } else {
            // whole string less than 16B
            // if this is common, try 64b or even 32b cleanup with movq / movd and upcase_si128
#if 1
            for (unsigned int i = 0 ; i <= cleanup_bytes ; ++i) {
                dst[i] = ascii_toupper(src[i]);
            }
#else
            // gcc stupidly auto-vectorizes this, resulting in huge code bloat, but no measurable slowdown because it never runs
            for (int i = cleanup_bytes - 1 ;  i >= 0 ; --i) {
                dst[i] = ascii_toupper(src[i]);
            }
#endif
        }
    }

    return last_byte - src_begin;
}

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.

  • String de 15 caracteres: linha de base: 1.08s. autovec: 1.34s. manual: 1.29s
  • String de 16 caracteres: linha de base: 1.16s. autovec: 1.52s. manual: 0.335s
  • String de 31 caracteres: manual: 0.479s
  • 127 string de caracteres: linha de base: 8.91s. autovec: 2.98s. manual: 0.925s
  • Cadeia de caracteres de 128 caracteres: linha de base: 9.00s. autovec: 2.06s. manual: 0.931s
  • Sequência de caracteres de 129 caracteres: linha de base: 9.04s. autovec: 2.07s. manual: 1.02s

(Na verdade, com o tempo programado _mm_storeno 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 movqou movdcargas / 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 dstponto pode avançar em uma quantidade diferente do srcponteiro, mas quando voltarmos a um srcponteiro alinhado , ainda faremos armazenamentos de vetor desalinhados dst.

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 correto toupper('i')é 'İ'(U0130), não 'I'(ASCII simples). Veja os comentários de Martin Bonner sobre uma pergunta sobre tolower()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 PCMPISTRMou algo assim pode ser capaz de realizar muitas de nossas verificações de uma só vez.


20

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


7
Ou o eszet alemão (sp?), Algo que se parece com a letra grega beta e significa "ss". Não existe um caractere alemão único que significa "SS", que é o equivalente em maiúsculas. A palavra alemã para "rua", quando maiúscula, recebe um caractere mais.
9139 David Thornley

6
Outro caso especial é a letra grega sigma (Σ), que tem duas versões em minúsculas, dependendo se está no final de uma palavra (ς) ou não (σ). E existem regras específicas de idioma, como o turco, com o mapeamento de casos I caseı e İ↔i.
Dan04

1
"Maiúsculas" é chamado de dobra em caixa.
Columbo

20
string StringToUpper(string strToConvert)
{
   for (std::string::iterator p = strToConvert.begin(); strToConvert.end() != p; ++p)
       *p = toupper(*p);

   return p;
}

Ou,

string StringToUpper(string strToConvert)
{
    std::transform(strToConvert.begin(), strToConvert.end(), strToConvert.begin(), ::toupper);

    return strToConvert;
}

4
se você não tem acesso para aumentar a segunda solução, provavelmente é o melhor que pode obter. o que as estrelas **após os parâmetros na primeira solução fazem?
Sam Brinck

1
Eu tenho certeza que **é um erro de digitação que resta de tentar usar fonte em negrito na sintaxe do código.
MasterHD

1
Esse código chama um comportamento indefinido quando toupperé chamado com números negativos.
Roland Illig 31/03/19

17

O seguinte funciona para mim.

#include <algorithm>
void  toUpperCase(std::string& str)
{
    std::transform(str.begin(), str.end(), str.begin(), ::toupper);
}

int main()
{
   std::string str = "hello";
   toUpperCase(&str);
}

Observe que std :: transform é definido em <algorithm> #
301

Sim. esse # include é obrigatório, #include <algorithm> #
Pabitra Dash 28/04

1
Esse código chama um comportamento indefinido quando toupperé chamado com números negativos.
Roland Illig 31/03/19

duplicado da resposta dada por user648545 - -1
Piotr Dobrogost

@PiotrDobrogost Não faço idéia da resposta dada pelo usuário648545. Eu não copiei isso. Quando comparo dois métodos, a assinatura do método é totalmente diferente, embora ambas as funções usem a função de transformação de biblioteca.
Pabitra Dash 27/09/18

13

Use uma lambda.

std::string s("change my case");

auto to_upper = [] (char_t ch) { return std::use_facet<std::ctype<char_t>>(std::locale()).toupper(ch); };

std::transform(s.begin(), s.end(), s.begin(), to_upper);

2
Byron, não se preocupe com os outros comentários. Não há problema em responder perguntas antigas com a nova solução (moderna), como você fez.
Kyberias

13

O mais rápido se você usar apenas caracteres ASCII :

for(i=0;str[i]!=0;i++)
  if(str[i]<='z' && str[i]>='a')
    str[i]-=32;

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


1
A pergunta está marcada como C++, mas você escreveu uma Cresposta aqui. (Eu não sou um dos downvoters.)
hkBattousai

6
Eu escrevi uma resposta C e uma resposta C ++ aqui beacuse C ++ é escrito para ser totalmente compatível com fontes de C, portanto, qualquer solução C é também uma solução C ++ correta
Luca C.

Mas é muito melhor dar uma resposta que respeite o modo C ++.
Dmitriy Yurchenko

A maneira padrão do c ++ seria usar std :: transform com toupper. Isso é menos código e com certeza é portátil. Esse código depende do "fato" de que o sistema usará ascii como o mecanismo de codificação de caracteres. Não tenho certeza se todos os sistemas são baseados nessa codificação e, portanto, não tenho certeza de que seja portátil.
AlexTheo # 14/17

1
Por que você decidiu usar códigos ASCII em vez de caracteres entre '?
HolyBlackCat

11

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:

void strtoupper(char* str)
{ 
    while (*str) *(str++) = toupper((unsigned char)*str);
}

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.


Observa-se que esta resposta é para cordas ac ao invés de um std :: string
EvilTeach

Isso tem uma falha de segurança inerente óbvia. Eu não faria isso.
Byron

9
//works for ASCII -- no clear advantage over what is already posted...

std::string toupper(const std::string & s)
{
    std::string ret(s.size(), char());
    for(unsigned int i = 0; i < s.size(); ++i)
        ret[i] = (s[i] <= 'z' && s[i] >= 'a') ? s[i]-('a'-'A') : s[i];
    return ret;
}

O s.size () é do tipo std :: size_t, o qual o AFAIK pode muito bem ser
desassinado

Eu não acho que existem implementações modernas nas quais o resultado de std :: string :: size seja assinado. Dado que, semântica e praticamente, não existe tamanho negativo, eu vou usar size_t como pelo menos um número inteiro não assinado de 32 bits.
usar o seguinte comando

Não há razão para não escrever 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.
Peter Cordes

9
#include <string>
#include <locale>

std::string str = "Hello World!";
auto & f = std::use_facet<std::ctype<char>>(std::locale());
f.toupper(str.data(), str.data() + str.size());

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:

std::use_facet<std::ctype<char> > & f = 
    std::use_facet<std::ctype<char> >(std::locale());
f.toupper(const_cast<char *>(str.data()), str.data() + str.size());

7
typedef std::string::value_type char_t;

char_t up_char( char_t ch )
{
    return std::use_facet< std::ctype< char_t > >( std::locale() ).toupper( ch );
}

std::string toupper( const std::string &src )
{
    std::string result;
    std::transform( src.begin(), src.end(), std::back_inserter( result ), up_char );
    return result;
}

const std::string src  = "test test TEST";

std::cout << toupper( src );

não recomendaria um back_inserter como você já sabe o comprimento; use std :: string result (src.size ()); std :: transform (src.begin (), src.end (), resultado.begin (), up_char);
Viktor Sehr

Embora eu tenha certeza que você sabe disso.
Viktor Sehr

@ Viktor Sehr, @ bayda: Eu sei que isso tem 2 anos, mas por que não tirar o melhor dos dois mundos? Use reservee back_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; }
Evan Teran

4
std::string value;
for (std::string::iterator p = value.begin(); value.end() != p; ++p)
    *p = toupper(*p);

Esse código chama um comportamento indefinido quando toupperé chamado com números negativos.
Roland Illig

2

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 string


Essa sugestão chama um comportamento indefinido quando toupperé chamada com números negativos. Você deveria ter mencionado o elenco necessário unsigned char.
Roland Illig 31/03/19

2

Aqui está o código mais recente com o C ++ 11

std::string cmd = "Hello World";
for_each(cmd.begin(), cmd.end(), [](char& in){ in = ::toupper(in); });

Esse código chama um comportamento indefinido quando toupperé chamado com números negativos.
Roland Illig 31/03

1

Usando Boost.Text, que funcionará para texto Unicode

boost::text::text t = "Hello World";
boost::text::text uppered;
boost::text::to_title(t, std::inserter(uppered, uppered.end()));
std::string newstr = uppered.extract();

1

A resposta de @dirkgently é muito inspiradora, mas quero enfatizar que, devido à preocupação mostrada abaixo,

Como todas as outras funções de, o comportamento de std :: toupper é indefinido se o valor do argumento não for representável como char não assinado nem igual a EOF. Para usar essas funções com segurança com caracteres simples (ou caracteres assinados), o argumento deve primeiro ser convertido em caracteres não assinados.
Referência : std :: toupper

o uso correto de std::toupperdeve ser:

#include <algorithm>
#include <cctype>
#include <iostream>
#include <iterator>
#include <string>

void ToUpper(std::string& input)
{
    std::for_each(std::begin(input), std::end(input), [](char& c) {
        c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
    });
}

int main()
{
    std::string s{ "Hello world!" };
    std::cout << s << std::endl;
    ::ToUpper(s);
    std::cout << s << std::endl;

    return 0;
}

Resultado:

Hello world!
HELLO WORLD!

0

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.

string StringToUpper(string strToConvert)
{//change each element of the string to upper case
   for(unsigned int i=0;i<strToConvert.length();i++)
   {
      strToConvert[i] = toupper(strToConvert[i]);
   }
   return strToConvert;//return the converted string
}

string StringToLower(string strToConvert)
{//change each element of the string to lower case
   for(unsigned int i=0;i<strToConvert.length();i++)
   {
      strToConvert[i] = tolower(strToConvert[i]);
   }
   return strToConvert;//return the converted string
}

.length () não é do tipo 'unsigned int'
malat 5/02/2014

Esse código chama um comportamento indefinido quando toupperé chamado com números negativos.
Roland Illig 31/03

0

Minha solução (limpando o sexto bit para alfa):

#include <ctype.h>

inline void toupper(char* str)
{
    while (str[i]) {
        if (islower(str[i]))
            str[i] &= ~32; // Clear bit 6 as it is what differs (32) between Upper and Lowercases
        i++;
    }
}

Esse código chama um comportamento indefinido quando toupperé chamado com números negativos.
Roland Illig 31/03/19

Não ... Verifique se você está certo antes da votação. Islower só funcionaria em valores não negativos ...
Antonin GAVREL

-1

Todas essas soluções nesta página são mais difíceis do que precisam.

Faça isso

RegName = "SomE StRing That you wAnt ConvErTed";
NameLength = RegName.Size();
for (int forLoop = 0; forLoop < NameLength; ++forLoop)
{
     RegName[forLoop] = tolower(RegName[forLoop]);
}

RegNameé seu string. Obtenha seu tamanho de string, não use string.size()como seu testador real, muito confuso e pode causar problemas. então. o forloop 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


4
Não vejo como isso é mais simples que a solução boost :: toupper. Você pode elaborar?
tr9sh

2
Já existem muitos tolowerloops simples , e a maioria deles usa nomes de variáveis ​​de loop padrão i, como não estranhos forLoop.
Peter Cordes

-1

Sem usar nenhuma biblioteca:

std::string YourClass::Uppercase(const std::string & Text)
{
    std::string UppperCaseString;
    UppperCaseString.reserve(Text.size());
    for (std::string::const_iterator it=Text.begin(); it<Text.end(); ++it)
    {
        UppperCaseString.push_back(((0x60 < *it) && (*it < 0x7B)) ? (*it - static_cast<char>(0x20)) : *it);
    }
    return UppperCaseString;
}

O código acima funciona apenas para codificações compatíveis com ASCII. Nem a pergunta, nem a sua resposta mencionam essa restrição. Um deles deveria.
Roland Illig 31/03/19

-1

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.

template<int ...Is>
struct IntVector{
using Type = IntVector<Is...>;
};

template<typename T_Vector, int I_New>
struct PushFront;
template<int ...Is, int I_New>
struct PushFront<IntVector<Is...>,I_New> : IntVector<I_New,Is...>{};

template<int I_Size, typename T_Vector = IntVector<>>
struct Iota : Iota< I_Size-1, typename PushFront<T_Vector,I_Size-1>::Type> {};
template<typename T_Vector>
struct Iota<0,T_Vector> : T_Vector{};

template<char C_In>
struct ToUpperTraits {
    enum { value = (C_In >= 'a' && C_In <='z') ? C_In - ('a'-'A'):C_In };
};

template<typename T>
struct TableToUpper;
template<int ...Is>
struct TableToUpper<IntVector<Is...>>{
    static char at(const char in){
        static const char table[] = {ToUpperTraits<Is>::value...};
        return table[in];
    }
};

int tableToUpper(const char c){
    using Table = TableToUpper<typename Iota<256>::Type>;
    return Table::at(c);
}

com caso de uso:

std::transform(in.begin(),in.end(),out.begin(),tableToUpper);

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


-1
template<size_t size>
char* toupper(char (&dst)[size], const char* src) {
    // generate mapping table once
    static char maptable[256];
    static bool mapped;
    if (!mapped) {
        for (char c = 0; c < 256; c++) {
            if (c >= 'a' && c <= 'z')
                maptable[c] = c & 0xdf;
            else
                maptable[c] = c;
        }
        mapped = true;
    }

    // use mapping table to quickly transform text
    for (int i = 0; *src && i < size; i++) {
        dst[i] = maptable[*(src++)];
    }
    return dst;
}

-1

Essa função c ++ sempre retorna a cadeia de caracteres em maiúsculas ...

#include <locale> 
#include <string>
using namespace std; 
string toUpper (string str){
    locale loc; 
    string n; 
    for (string::size_type i=0; i<str.length(); ++i)
        n += toupper(str[i], loc);
    return n;
}

-3

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.

void to_upper(const std::string str) {
    std::string::iterator it;
    int i;
    for ( i=0;i<str.size();++i ) {
        ((char *)(void *)str.data())[i]=toupper(((char *)str.data())[i]);
    }
}

I know you're not supposed to modify that data area- qual área de dados você não deve modificar?
user93353

3
É tarde, mas o que diabos? Essa linha maluca pode ser substituída por str[i] = toupper(str[i]);perfeitamente fina (bem, não perfeitamente fina, mas corrige a maioria das coisas erradas).
chris
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.