Convertir una cadena en C ++ a mayúsculas

268

¿Cómo se puede convertir una cadena a mayúsculas? Los ejemplos que he encontrado en google solo tienen que ver con caracteres.

NaranjaAlmendraJabón
fuente

Respuestas:

205

Impulsar algoritmos de cadena:

#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");
Tony Edgecombe
fuente
55
Esto también tiene el beneficio de i18n, donde ::toupperprobablemente asume ASCII.
Ben Straub
44
Su última línea no se compila: debe cambiar a algo como:std::string newstr(boost::to_upper_copy<std::string>("Hello World"));
maxschlepzig
58
esta no debería ser la respuesta aceptada, ya que requiere un impulso, o el título debe cambiarse.
Andrea
44
sí, voy a instalar boost solo por to_upper ... ¡excelente idea! </ sarcasmo> :)
Thang
12
Personalmente estoy mal dispuesto hacia el impulso como respuesta a "¿cómo hago x en C ++?" porque boost no es una solución ligera en absoluto. Parece que usted compra en boost como marco (o ACE o Qt o Recusion ToolKit ++ o ...) o no lo hace. Prefiero ver soluciones de lenguaje.
jwm
486
#include <algorithm>
#include <string>

std::string str = "Hello World";
std::transform(str.begin(), str.end(),str.begin(), ::toupper);
Pierre
fuente
8
En realidad, toupper()se puede implementar como una macro. Esto puede causar un problema.
Dirkgently
3
un enlace (:: toupper, construcción <unsigned char> (_ 1)) con boost.lambda servirá perfectamente bien, creo.
Johannes Schaub - litb
11
Este enfoque funciona bien para ASCII, pero falla para codificaciones de caracteres de varios bytes, o para reglas especiales de mayúsculas como 'ß' en alemán.
dan04
9
Cambié la respuesta aceptada a la que usaba las bibliotecas de impulso, porque era más rápido (en mis pruebas informales), más fácil de usar y no tenía los problemas asociados con esta solución. Sigue siendo una buena solución para los casos en los que no se puede usar boost.
OrangeAlmondSoap
2
No puedo entender por qué el compilador rechaza este código sin :: calificador antes toupper. ¿Algunas ideas?
sasha.sochka
90

Solución corta usando C ++ 11 y toupper ().

for (auto & c: str) c = toupper(c);
Thanasis Papoutsidakis
fuente
¿No csería del const chartipo (de auto)? Si es así, no puede asignarlo (debido a una constparte) a lo que devuelve toupper(c).
PolGraphic
55
@PolGraphic: Rango: basado en el uso de los métodos begin () / end () del contenedor para iterar sobre su contenido. std :: basic_string tiene un iterador constante y mutable (devuelto por cbegin () y begin () respectivamente, vea std :: basic_string :: begin ), por lo que para (:) usa el apropiado (cbegin () si str es declarado const, con auto =: = const char, begin () de lo contrario, con auto =: = char).
Thanasis Papoutsidakis
55
Vea la respuesta de dirkgently a continuación, cnecesita ser lanzado unsigned charpara que esto sea corregido.
Cris Luengo
boost's to_upper () parece mucho más consistente con las funciones c ++ STL que toupper.
tartaruga_casco_mole
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: Un par de problemas con la solución principal:

21.5 Utilidades de secuencia terminadas en nulo

El contenido de estos encabezados debe ser el mismo que el de los encabezados de la Biblioteca C estándar <ctype.h>, <wctype.h>, <string.h>, <wchar.h> y <stdlib.h> [...]

  • Lo que significa que los cctypemiembros pueden ser macros no aptos para el consumo directo en algoritmos estándar.

  • Otro problema con el mismo ejemplo es que no arroja el argumento ni verifica que esto no sea negativo; Esto es especialmente peligroso para los sistemas en los que charse firma sin formato . (La razón es: si esto se implementa como una macro, probablemente usará una tabla de búsqueda y sus argumentos indexados en esa tabla. Un índice negativo le dará UB).

Dirkgently
fuente
Los miembros normales de cctype son macros. Recuerdo haber leído que también tenían que ser funciones, aunque no tengo una copia del estándar C90 y no sé si se indicó explícitamente o no.
David Thornley
1
tienen que ser funciones en C ++, incluso si C les permite ser macros. Sin embargo, estoy de acuerdo con tu segundo punto sobre el casting. la solución superior podría pasar valores negativos y causar UB con eso. esa es la razón por la que no lo voté (pero tampoco lo
rechacé
1
no debe faltar la cotización estándar: 7.4.2.2/1 (litb pobre, solo hace referencia a un borrador C99 TC2) y C ++ 17.4.1.2/6 en el estándar gloria c ++ 98.
Johannes Schaub - litb
1
(tenga en cuenta la nota al pie: "Esto no permite la práctica común de proporcionar una macro de enmascaramiento ... bla, blupp ... la única forma de hacerlo en C ++ es proporcionar una función en línea externa") :)
Johannes Schaub - litb
1
... que se logra con este truco: stackoverflow.com/questions/650461/…
Johannes Schaub - litb
27

Este problema es vectorizable con SIMD para el conjunto de caracteres ASCII.


Comparaciones de aceleración:

Pruebas preliminares con x86-64 gcc 5.2 -O3 -march=nativeen un Core2Duo (Merom). La misma cadena de 120 caracteres (ASCII minúscula y no minúscula mixta), convertida en un bucle 40M veces (sin línea cruzada entre archivos, por lo que el compilador no puede optimizar ni sacar nada del bucle). Las mismas memorias intermedias de origen y destino, por lo que no hay sobrecarga malloc o efectos de memoria / caché: los datos están calientes en la caché L1 todo el tiempo, y estamos puramente vinculados a la CPU.

  • boost::to_upper_copy<char*, std::string>(): 198.0s . Sí, Boost 1.58 en Ubuntu 15.10 es realmente tan lento. Realicé un perfil y escalé el asm en un depurador, y es muy, muy malo: ¡hay una transmisión dinámica de una variable local por carácter! (Dynamic_cast toma múltiples llamadas a strcmp). Esto sucede con LANG=Cy con LANG=en_CA.UTF-8.

    No probé usando un RangeT que no sea std :: string. Tal vez la otra forma deto_upper_copy optimiza mejor, pero creo que lo hará siempre new/ mallocespacio para la copia, por lo que es más difícil de probar. Tal vez algo que hice difiere de un caso de uso normal, y tal vez normalmente detenido g ++ puede levantar el material de configuración regional del bucle por carácter. Mi ciclo de lectura de a std::stringy escritura en a char dstbuf[4096]tiene sentido para las pruebas.

  • loop call glibc toupper: 6.67s (sin embargo, no se verifica el intresultado de un posible UTF-8 de varios bytes. Esto es importante para el turco).

  • Bucle solo ASCII: 8.79s (mi versión de línea de base para los resultados a continuación). Aparentemente, una búsqueda de tabla es más rápida que a cmov, con la tabla activa en L1 de todos modos.
  • Auto-vectorizado solo ASCII: 2.51s . (120 caracteres están a medio camino entre el peor y el mejor de los casos, ver más abajo)
  • ASCII solo vectorizado manualmente: 1.35s

Consulte también esta pregunta sobre la toupper()lentitud en Windows cuando se establece una configuración regional .


Me sorprendió que Boost sea un orden de magnitud más lento que las otras opciones. Verifiqué dos veces que había -O3habilitado, e incluso un solo paso para ver lo que estaba haciendo. Es casi exactamente la misma velocidad con clang ++ 3.8. Tiene una gran sobrecarga dentro del bucle por personaje. El perf record/ reportresultado (para el cyclesevento perf) es:

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

Autovectorización

Gcc y clang solo vectorizarán automáticamente los bucles cuando el recuento de iteraciones se conozca antes del bucle. (es decir, los bucles de búsqueda como la implementación de C simple de strlenno se autovectorizarán).

Por lo tanto, para las cadenas lo suficientemente pequeñas como para caber en la memoria caché, obtenemos una aceleración significativa para las cadenas de ~ 128 caracteres de largo de hacer strlenprimero. Esto no será necesario para cadenas de longitud explícita (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;
}

Cualquier libc decente tendrá una eficiencia strlenmucho más rápida que el bucle de un byte a la vez, por lo que los bucles strlen y toupper vectorizados por separado son más rápidos.

Línea de base: un bucle que comprueba un 0 final sobre la marcha.

Tiempos para iteraciones de 40M, en un Core2 (Merom) 2.4GHz. gcc 5.2 -O3 -march=native. (Ubuntu 15.10). dst != src(así que hacemos una copia), pero no se superponen (y no están cerca). Ambos están alineados.

  • Cadena de 15 caracteres: línea de base: 1.08s. autovec: 1.34s
  • Cadena de 16 caracteres: línea base: 1.16s. autovec: 1.52s
  • 127 cadena de caracteres: línea de base: 8.91s. autovec: 2.98s // la limpieza no vectorial tiene 15 caracteres para procesar
  • Cadena de 128 caracteres: línea de base: 9.00s. autovec: 2.06s
  • 129 cadena de caracteres: línea de base: 9.04s. autovec: 2.07s // la limpieza no vectorial tiene 1 carácter para procesar

Algunos resultados son un poco diferentes con el sonido metálico.

El bucle de microbenchmark que llama a la función está en un archivo separado. De lo contrario, se alinea y strlen()se saca del circuito, y se ejecuta dramáticamente más rápido, especialmente. para 16 cadenas de caracteres (0.187s).

Esto tiene la gran ventaja de que gcc puede auto-vectorizarlo para cualquier arquitectura, pero la mayor desventaja es que es más lento para el caso generalmente común de cadenas pequeñas.


Entonces, hay grandes aceleraciones, pero la vectorización automática del compilador no es un gran código, especialmente. para la limpieza de los últimos hasta 15 caracteres.

Vectorización manual con intrínsecos SSE:

Basado en mi función de cambio de mayúsculas y minúsculas que invierte las mayúsculas y minúsculas de cada carácter alfabético. Aprovecha el "truco de comparación sin signo", donde puedes hacer low < a && a <= highuna sola comparación sin signo mediante el desplazamiento de rango, de modo que cualquier valor menor que se lowajusta a un valor mayor que high. (Esto funciona si lowy highno están demasiado separados).

SSE solo tiene una comparación con signo mayor, pero aún podemos usar el truco de "comparación sin signo" al cambiar el rango al final del rango firmado: Reste 'a' + 128, por lo que los caracteres alfabéticos varían de -128 a -128 +25 (-128 + 'z' - 'a')

Tenga en cuenta que sumar 128 y restar 128 son lo mismo para los enteros de 8 bits. No hay a dónde ir el transporte, por lo que es solo xor (complemento sin transporte), volteando el bit alto.

#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 esta función que funciona para un vector, podemos llamarlo en un bucle para procesar una cadena completa. Como ya estamos apuntando a SSE2, podemos hacer una verificación de fin de cadena vectorizada al mismo tiempo.

También podemos hacerlo mucho mejor para la "limpieza" de los últimos 15 bytes restantes después de hacer los vectores de 16B: la mayúscula es idempotente, por lo que volver a procesar algunos bytes de entrada está bien. Hacemos una carga no alineada de los últimos 16B de la fuente y la almacenamos en el búfer de destino superponiendo la última tienda de 16B del bucle.

El único momento en que esto no funciona es cuando toda la cadena está por debajo de 16B: incluso cuando dst=srcla lectura-modificación-escritura no atómica no es lo mismo que no tocar algunos bytes, y puede romper el código multiproceso.

Tenemos un bucle escalar para eso, y también para srcalinearnos. Como no sabemos dónde estará la terminación 0, una carga desalineada srcpodría pasar a la página siguiente y volverse por defecto. Si necesitamos bytes en un bloque 16B alineado, siempre es seguro cargar todo el bloque 16B alineado.

Fuente completa: en un github gist .

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

Tiempos para iteraciones de 40M, en un Core2 (Merom) 2.4GHz. gcc 5.2 -O3 -march=native. (Ubuntu 15.10). dst != src(así que hacemos una copia), pero no se superponen (y no están cerca). Ambos están alineados.

  • Cadena de 15 caracteres: línea de base: 1.08s. autovec: 1.34s. manual: 1.29s
  • Cadena de 16 caracteres: línea de base: 1.16s. autovec: 1.52s. manual: 0.335s
  • Cadena de 31 caracteres: manual: 0.479s
  • 127 cadena de caracteres: línea de base: 8.91s. autovec: 2.98s. manual: 0.925s
  • Cadena de 128 caracteres: línea de base: 9.00s. autovec: 2.06s. manual: 0.931s
  • 129 cadena de caracteres: línea de base: 9.04s. autovec: 2.07s. manual: 1.02s

(Realmente cronometrado con _mm_storeel bucle, no _mm_storeu, porque storeu es más lento en Merom incluso cuando la dirección está alineada. Está bien en Nehalem y más tarde. También dejé el código tal como está por ahora, en lugar de arreglar el error al copiar la terminación de 0 en algunos casos, porque no quiero volver a cronometrar todo).

Entonces, para cadenas cortas de más de 16B, esto es dramáticamente más rápido que el vectorizado automático. Las longitudes uno-menos-que-un-ancho-vector no presentan un problema. Pueden ser un problema al operar en el lugar, debido a un puesto de reenvío de la tienda. (Pero tenga en cuenta que todavía está bien procesar nuestra propia salida, en lugar de la entrada original, porque toupper es idempotente).

Hay muchas posibilidades para ajustar esto para diferentes casos de uso, dependiendo de lo que quiera el código circundante y la microarquitectura objetivo. Conseguir que el compilador emita un buen código para la parte de limpieza es complicado. Usar ffs(3)(que se compila en bsf o tzcnt en x86) parece ser bueno, pero obviamente ese bit necesita un replanteamiento ya que noté un error después de escribir la mayor parte de esta respuesta (ver los comentarios de FIXME).

Aceleraciones vectoriales para cadenas aún más pequeñas se pueden obtener con movqo movdcargas / tiendas. Personalice según sea necesario para su caso de uso.


UTF-8:

Podemos detectar cuándo nuestro vector tiene bytes con el conjunto de bits alto, y en ese caso recurrir a un bucle escalar utf-8-aware para ese vector. El dstpunto puede avanzar en una cantidad diferente al srcpuntero, pero una vez que volvamos a un srcpuntero alineado , seguiremos haciendo almacenamientos de vectores no alineados dst.

Para el texto que es UTF-8, pero que consiste principalmente en el subconjunto ASCII de UTF-8, esto puede ser bueno: alto rendimiento en el caso común con comportamiento correcto en todos los casos. Sin embargo, cuando hay una gran cantidad de no ASCII, probablemente sea peor que permanecer todo el tiempo en el circuito consciente escalar UTF-8.

Hacer que el inglés sea más rápido a expensas de otros idiomas no es una decisión a prueba de futuro si el inconveniente es significativo.


Locale-aware:

En el entorno local turco ( tr_TR), el resultado correcto toupper('i')es 'İ'(U0130), no 'I'(ASCII simple). Vea los comentarios de Martin Bonner sobre una pregunta acerca de tolower()ser lento en Windows.

También podemos verificar si hay una lista de excepciones y una reserva para escalar allí, como para los caracteres de entrada UTF8 de varios bytes.

Con tanta complejidad, SSE4.2 PCMPISTRMo algo así podría hacer muchas de nuestras comprobaciones de una sola vez.

Peter Cordes
fuente
20

¿Tiene caracteres ASCII o internacionales en cadenas?

Si es el último caso, "mayúsculas" no es tan simple, y depende del alfabeto utilizado. Hay alfabetos bicamerales y unicamerales. Solo los alfabetos bicamerales tienen caracteres diferentes para mayúsculas y minúsculas. Además, hay caracteres compuestos, como la letra mayúscula latina 'DZ' (\ u01F1 'DZ') que utilizan el llamado caso de título . Esto significa que solo se cambia el primer carácter (D).

Le sugiero que busque en la UCI y la diferencia entre las asignaciones de casos simples y completas. Esto podría ayudar:

http://userguide.icu-project.org/transforms/casemappings

Milan Babuškov
fuente
77
O el eszet alemán (sp?), Lo que se parece a la letra beta griega y significa "ss". No hay un solo carácter alemán que signifique "SS", que es el equivalente en mayúsculas. La palabra alemana para "calle", en mayúscula, tiene un carácter más largo.
David Thornley
66
Otro caso especial es la letra griega sigma (Σ), que tiene dos versiones en minúsculas, dependiendo de si está al final de una palabra (ς) o no (σ). Y luego están las reglas específicas del idioma, como el turco que tiene el mapeo de casos I↔ı e İ↔i.
dan04
1
"Mayúsculas" se llama plegado de mayúsculas y minúsculas.
Columbo
20
string StringToUpper(string strToConvert)
{
   for (std::string::iterator p = strToConvert.begin(); strToConvert.end() != p; ++p)
       *p = toupper(*p);

   return p;
}

O,

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

    return strToConvert;
}
usuario648545
fuente
44
Si no tiene acceso para impulsar, la segunda solución es probablemente la mejor que puede obtener. ¿Qué hacen las estrellas **después de los parámetros en la primera solución?
Sam Brinck
1
Estoy bastante seguro de que **es un error tipográfico al intentar usar una fuente en negrita en la sintaxis del código.
MasterHD
1
Este código invoca un comportamiento indefinido cuando toupperse llama con números negativos.
Roland Illig
17

Lo siguiente funciona para mí.

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

int main()
{
   std::string str = "hello";
   toUpperCase(&str);
}
Pabitra Dash
fuente
Tenga en cuenta que std :: transform se define en <algorithm>
edj
Si. este # include es obligatorio, #include <algorithm>
Pabitra Dash el
1
Este código invoca un comportamiento indefinido cuando toupperse llama con números negativos.
Roland Illig
duplicado de la respuesta dada por el usuario648545 - -1
Piotr Dobrogost
@PiotrDobrogost No tengo idea de la respuesta dada por el usuario648545. No he copiado eso. Cuando comparo dos métodos, la firma del método es completamente diferente, aunque ambas funciones usan la función de biblioteca de transformación.
Pabitra Dash
13

Usa una 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);
Byron
fuente
2
Byron, no te preocupes por los otros comentarios. Está bien responder viejas preguntas con una solución nueva (moderna) como lo hizo.
Kyberias
13

El más rápido si usa solo caracteres ASCII :

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

Tenga en cuenta que este código se ejecuta más rápido pero solo funciona en ASCII y no es una solución "abstracta".

Si necesita soluciones UNICODE o soluciones más convencionales y abstractas, busque otras respuestas y trabaje con métodos de cadenas C ++.

Luca C.
fuente
1
La pregunta está etiquetada como C++, pero escribió una Crespuesta aquí. (No soy uno de los
votantes negativos
66
Escribí una respuesta C y una respuesta C ++ aquí porque C ++ está escrito para ser totalmente compatible con las fuentes C, por lo que cualquier solución C también es una solución correcta C ++
Luca C.
Pero es mucho mejor dar una respuesta que respete la forma de C ++.
Dmitriy Yurchenko
La forma estándar de c ++ sería usar std :: transform con toupper. Eso es menos código y seguro portátil. Este código se basa en el "hecho" de que el sistema utilizará ascii como mecanismo de codificación de caracteres. No estoy seguro de que todos los sistemas se basen en esta codificación y, por lo tanto, no estoy seguro de que sea portátil.
AlexTheo
1
¿Por qué decidió usar códigos ASCII en lugar de caracteres encerrados '?
HolyBlackCat
11

Siempre que esté bien con ASCII solo y pueda proporcionar un puntero válido a la memoria RW, hay una línea simple y muy efectiva en C:

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

Esto es especialmente bueno para cadenas simples como identificadores ASCII que desea normalizar en el mismo caso de caracteres. Luego puede usar el búfer para construir una instancia de std: string.

k3a
fuente
Uno observa que esta respuesta es para una cadena de CA en lugar de una std :: string
EvilTeach
Esto tiene una obvia falla de seguridad inherente. Yo no haría esto.
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;
}
David
fuente
s.size () es de tipo std :: size_t que, AFAIK bien podría estar sin firmar int dependiendo de la implementación
odinthenerd
No creo que haya implementaciones modernas en las que se firme el resultado de std :: string :: size. Dado que, semántica y prácticamente, no existe un tamaño negativo, voy a ir con size_t siendo al menos un entero sin signo de 32 bits.
user1329482
No hay razón para no escribir for (size_t i = 0 .... Tampoco hay una buena razón para que sea tan difícil de leer. Esto también copia la cadena primero y luego repite sobre ella. La respuesta de @ Luke es mejor en algunos aspectos, excepto por no aprovechar las 'a'constantes de los personajes.
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());

Esto funcionará mejor que todas las respuestas que usan la función global toupper, y es presumiblemente lo que boost :: to_upper está haciendo debajo.

Esto se debe a que :: toupper tiene que buscar el entorno local, porque podría haber sido cambiado por un hilo diferente, para cada invocación, mientras que aquí solo la llamada a locale () tiene esta penalización. Y buscar la configuración regional generalmente implica tomar un candado.

Esto también funciona con C ++ 98 después de reemplazar el auto, usar los nuevos str.data () no constantes y agregar un espacio para romper el cierre de la plantilla (">>" a ">>") así:

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());
Glen Knowles
fuente
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 );
bayda
fuente
no recomendaría un back_inserter ya que ya conoce la longitud; use std :: string result (src.size ()); std :: transform (src.begin (), src.end (), result.begin (), up_char);
Viktor Sehr
Aunque estoy seguro de que lo sabes.
Viktor Sehr
@Viktor Sehr, @bayda: Sé que esto tiene 2 años, pero ¿por qué no obtener lo mejor de ambos mundos? Use reservey back_inserter(haciendo que la cadena solo se copie una 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 el
4
std::string value;
for (std::string::iterator p = value.begin(); value.end() != p; ++p)
    *p = toupper(*p);
Dmitriy Yurchenko
fuente
Este código invoca un comportamiento indefinido cuando toupperse llama con números negativos.
Roland Illig
2

pruebe la toupper()función ( #include <ctype.h>). acepta caracteres como argumentos, las cadenas están formadas por caracteres, por lo que tendrá que iterar sobre cada carácter individual que, cuando se junta, comprende la cadena

zmf
fuente
Esta sugerencia invoca un comportamiento indefinido cuando toupperse llama con números negativos. Deberías haber mencionado el elenco necesario para unsigned char.
Roland Illig
2

Aquí está el último código con C ++ 11

std::string cmd = "Hello World";
for_each(cmd.begin(), cmd.end(), [](char& in){ in = ::toupper(in); });
usuario2787620
fuente
Este código invoca un comportamiento indefinido cuando toupperse llama con números negativos.
Roland Illig
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();
Cor3ntin
fuente
1

La respuesta de @dirkgently es muy inspiradora, pero quiero enfatizar eso debido a la preocupación que se muestra a continuación,

Al igual que todas las demás funciones, el comportamiento de std :: toupper no está definido si el valor del argumento no es representable como char sin signo ni es igual a EOF. Para usar estas funciones de forma segura con caracteres simples (o caracteres firmados), el argumento primero se debe convertir a char sin firmar
Referencia : std :: toupper

El uso correcto de std::toupperdebe 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;
}

Salida:

Hello world!
HELLO WORLD!
KaiserKatze
fuente
0

No estoy seguro de que haya una función incorporada. Prueba esto:

Incluya las bibliotecas ctype.h o cctype, así como stdlib.h como parte de las directivas de preprocesador.

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
}
Brandon Stewart
fuente
.length () no es del tipo 'unsigned int'
malat
Este código invoca un comportamiento indefinido cuando toupperse llama con números negativos.
Roland Illig
0

Mi solución (borrar 6 bits 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++;
    }
}
Antonin GAVREL
fuente
Este código invoca un comportamiento indefinido cuando toupperse llama con números negativos.
Roland Illig
No ... Por favor verifique que esté justo antes de votar. Islower solo funcionaría con valores no negativos ...
Antonin GAVREL
-1

TODAS estas soluciones en esta página son más difíciles de lo necesario.

Hacer esto

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

RegNamees tu string. Obtenga el tamaño de su cadena no use string.size()como su probador real, muy desordenado y puede causar problemas. luego. El forbucle más básico .

recuerde que el tamaño de cadena también devuelve el delimitador, así que use <y no <= en su prueba de bucle.

la salida será: alguna cadena que quieras convertir

secreto
fuente
44
No veo cómo esto es más simple que la solución boost :: toupper. ¿Puedes elaborar?
tr9sh
2
Ya hay muchos tolowerbucles simples , y la mayoría de ellos usan nombres de variables de bucle estándar como i, no los extraños forLoop.
Peter Cordes
-1

Sin usar ninguna 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;
}
hkBattousai
fuente
El código anterior solo funciona para codificaciones compatibles con ASCII. Ni la pregunta ni su respuesta mencionan esta restricción. Uno de ellos debería.
Roland Illig
-1

Si solo le interesan los caracteres de 8 bits (que todas las otras respuestas, excepto Milan Babuškov también asumen), puede obtener la velocidad más rápida generando una tabla de búsqueda en tiempo de compilación utilizando metaprogramación. En ideone.com, esto funciona 7 veces más rápido que la función de biblioteca y 3 veces más rápido que una versión escrita a mano ( http://ideone.com/sb1Rup ). También es personalizable a través de rasgos sin ralentización.

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

con caso de uso:

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

Para una descripción en profundidad (muchas páginas) de cómo funciona, permítame conectar descaradamente mi blog: http://metaporky.blogspot.de/2014/07/part-4-generating-look-up-tables-at.html

odinthenerd
fuente
-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;
}
Ronny Gunawan
fuente
-1

Esta función de c ++ siempre devuelve la cadena de mayú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;
}
Awais Jameel
fuente
-3

Yo uso esta solución Sé que se supone que no debes modificar esa área de datos ... pero creo que eso es principalmente por errores de desbordamiento de búfer y caracteres nulos ... las cosas de mayúsculas no son lo mismo.

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]);
    }
}
Erik Aronesty
fuente
I know you're not supposed to modify that data area- ¿Qué área de datos no debes modificar?
user93353
3
Esto es tarde, pero ¿qué demonios? Esa línea loca se puede reemplazar con str[i] = toupper(str[i]);perfectamente bien (bueno, no perfectamente bien, pero corrige la mayoría de las cosas mal).
Chris