¿Cómo convertir std :: string a minúsculas?

777

Quiero convertir un std::stringa minúscula. Soy consciente de la función tolower(), sin embargo, en el pasado he tenido problemas con esta función y de todos modos no es lo ideal, ya que usarla con std::stringrequeriría iterar sobre cada carácter.

¿Existe alguna alternativa que funcione el 100% del tiempo?

Konrad
fuente
34
¿De qué otra manera convertiría cada elemento de una lista de algo en otra cosa, sin iterar a través de la lista? Una cadena es solo una lista de caracteres, si necesita aplicar alguna función a cada carácter, tendrá que iterar a través de la cadena. No hay forma de evitar eso.
14
¿Por qué exactamente esta pregunta reduce la calificación? No tengo ningún problema con iterar a través de mi cadena, pero estoy preguntando si hay otras funciones aparte de tolower (), toupper (), etc.
Konrad
3
Si tiene una matriz de caracteres de estilo C, entonces supongo que puede agregar ox20202020 a cada bloque de 4 caracteres (siempre que TODOS ya estén en mayúsculas) para convertir 4 caracteres en minúsculas a la vez.
13
@Dan: Si ya pueden estar en minúsculas, pero definitivamente son AZ o az, puede O con 0x20 en lugar de agregar. Una de esas optimizaciones tan inteligentes, probablemente tontas, que casi nunca valen la pena ...
Steve Jessop
44
No sé por qué habría sido rechazado ... ciertamente está redactado un poco extraño (porque de alguna manera tienes que recorrer cada elemento), pero es una pregunta válida
Warren

Respuestas:

906

Adaptado de preguntas no tan frecuentes :

#include <algorithm>
#include <cctype>
#include <string>

std::string data = "Abc";
std::transform(data.begin(), data.end(), data.begin(),
    [](unsigned char c){ return std::tolower(c); });

Realmente no vas a escapar sin recorrer cada personaje. No hay forma de saber si el carácter es minúscula o mayúscula.

Si realmente odias tolower(), aquí hay una alternativa ASCII especializada que no recomiendo que uses:

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

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

Tenga en cuenta que tolower()solo puede realizar una sustitución por carácter de un solo byte, lo que no es adecuado para muchos scripts, especialmente si se utiliza una codificación de varios bytes como UTF-8.

Stefan Mai
fuente
25
(Es posible que los algoritmos en cuestión hayan cambiado poco) @Stefan Mai: ¿Qué tipo de "sobrecarga" hay al llamar a los algoritmos STL? Las funciones son bastante esbeltas (es decir, simples para bucles) y a menudo están en línea, ya que rara vez tiene muchas llamadas a la misma función con los mismos parámetros de plantilla en la misma unidad de compilación.
eq-
257
Cada vez que asumes que los personajes son ASCII, Dios mata a un gatito. :(
Brian Gordon
13
Su primer ejemplo potencialmente tiene un comportamiento indefinido (pasar chara ::tolower(int)). Debe asegurarse de no pasar un valor negativo.
juanchopanza
37
-1 este uso de ::tolowerbien puede bloquearse, es UB para entrada no ASCII.
Saludos y hth. - Alf
77
El :: es necesario antes de una baja para indicar que está en el espacio de nombres más externo. Si usa este código en otro espacio de nombres, puede haber una definición diferente (posiblemente no relacionada) de tolower que terminaría siendo seleccionada preferentemente sin ::.
Charles Ofria
320

Boost proporciona un algoritmo de cadena para esto :

#include <boost/algorithm/string.hpp>

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

O, para no estar en el lugar :

#include <boost/algorithm/string.hpp>

const std::string str = "HELLO, WORLD!";
const std::string lower_str = boost::algorithm::to_lower_copy(str);
Robar
fuente
2
¿Supongo que esto no tiene los mismos problemas que una descarga con entrada ASCII?
Paul
19
Falla para no ASCII-7.
DevSolar
1
¿Existe una versión no implementada de esto?
Ray
55
@ Ray, sí,to_lower_copy
smac89
234

tl; dr

Use la biblioteca de la UCI . Si no lo hace, su rutina de conversión se interrumpirá silenciosamente en casos de los que probablemente ni siquiera esté al tanto.


Primero tienes que responder una pregunta: ¿Cuál es la codificación de tu std::string? ¿Es ISO-8859-1? O tal vez ISO-8859-8? ¿O la página de códigos de Windows 1252? ¿Lo que sea que esté usando para convertir mayúsculas a minúsculas lo sabe? (¿O falla miserablemente para los personajes 0x7f?)

Si está utilizando UTF-8 (la única opción sensata entre las codificaciones de 8 bits) std::stringcomo contenedor, ya se está engañando a sí mismo al creer que todavía tiene el control de las cosas, porque está almacenando una secuencia de caracteres multibyte en un contenedor que no conoce el concepto multibyte. Incluso algo tan simple como .substr()una bomba de relojería. (Debido a que dividir una secuencia multibyte dará como resultado una (sub) cadena no válida).

Y tan pronto como intente algo como std::toupper( 'ß' ), en cualquier codificación, estará en serios problemas. (Debido a que simplemente no es posible hacer esto "correctamente" con la biblioteca estándar, que solo puede entregar un carácter de resultado, no el "SS"necesario aquí.) [1] Otro ejemplo sería std::tolower( 'I' ), que debería producir resultados diferentes dependiendo de la configuración regional . En Alemania, 'i'sería correcto; en Turquía, 'ı'(LETRA PEQUEÑA LATINA DOTLESS I) es el resultado esperado (que, nuevamente, es más de un byte en la codificación UTF-8). Otro ejemplo es el Sigma griego , mayúsculas '∑', minúsculas 'σ'... excepto al final de una palabra, donde está 'ς'.

Entonces, cualquier conversión de caso que funcione en un personaje a la vez, o peor, un byte a la vez, se rompe por diseño.

Luego está el punto de que la biblioteca estándar, por lo que es capaz de hacer, depende de qué configuraciones regionales sean compatibles con la máquina en la que se está ejecutando su software ... ¿y qué hace si no lo es?

Entonces, lo que realmente está buscando es una clase de cadena que sea capaz de lidiar con todo esto correctamente, y esa no es ninguna de las std::basic_string<>variantes .

(Nota de C ++ 11: std::u16stringy std::u32stringson mejores , pero aún no son perfectos. C ++ 20 traído std::u8string, pero todo lo que hacen es especificar la codificación. En muchos otros aspectos, siguen ignorando la mecánica Unicode, como la normalización, la clasificación, ... .)

Mientras que Boost se ve bien, API sabia, Boost.Locale es básicamente un contenedor alrededor de la UCI . Si Boost se compila con soporte de ICU ... si no es así, Boost.Locale se limita al soporte de entorno local compilado para la biblioteca estándar.

Y créanme, hacer que Boost compile con UCI puede ser un verdadero dolor a veces. (No hay binarios precompilados para Windows, por lo que tendría que proporcionarlos junto con su aplicación, y eso abre una nueva lata de gusanos ...)

Entonces, personalmente, recomendaría obtener soporte completo de Unicode directamente de la boca del caballo y usar la biblioteca de la UCI directamente:

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

#include <iostream>

int main()
{
    /*                          "Odysseus" */
    char const * someString = u8"ΟΔΥΣΣΕΥΣ";
    icu::UnicodeString someUString( someString, "UTF-8" );
    // Setting the locale explicitly here for completeness.
    // Usually you would use the user-specified system locale,
    // which *does* make a difference (see ı vs. i above).
    std::cout << someUString.toLower( "el_GR" ) << "\n";
    std::cout << someUString.toUpper( "el_GR" ) << "\n";
    return 0;
}

Compilar (con G ++ en este ejemplo):

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

Esto da:

ὀδυσσεύς

Tenga en cuenta que la conversión Σ <-> σ en el medio de la palabra, y la conversión Σ <-> ς al final de la palabra. Ninguna <algorithm>solución basada en eso puede darte eso.


[1] En 2017, el Consejo de Ortografía Alemana dictaminó que "ẞ" U + 1E9E LATIN CAPITAL LETTER SHARP S podría usarse oficialmente, como una opción junto a la conversión tradicional "SS" para evitar la ambigüedad, por ejemplo, en pasaportes (donde los nombres están en mayúscula) ) Mi hermoso ejemplo, que quedó obsoleto por decisión del comité ...

DevSolar
fuente
19
Esta es la respuesta correcta en el caso general. El estándar no da nada para manejar nada excepto "ASCII" excepto mentiras y engaños. Te hace pensar que tal vez puedas lidiar con UTF-16, pero no puedes. Como dice esta respuesta, no puede obtener la longitud de caracteres adecuada (no la longitud de bytes) de una cadena UTF-16 sin hacer su propio manejo unicode. Si tiene que lidiar con texto real, use la UCI. Gracias, @DevSolar
Expiación limitada
¿ICU está disponible por defecto en Ubuntu / Windows o necesita instalarse por separado? Además, ¿qué tal esta respuesta: stackoverflow.com/a/35075839/207661 ?
Shital Shah
1
Hey, mira, una respuesta real! Gracias por señalarme en el directo correcto, DevSolar.
Dan Bechard
2
@DevSolar De acuerdo! El concepto de longitud no tiene sentido en el texto (podríamos agregar ligaduras a la lista de delincuentes). Dicho esto, dado que las personas están acostumbradas a pestañas y caracteres de control que ocupan una unidad de longitud, los puntos de código serían la medida más intuitiva. Ah, y gracias por dar la respuesta correcta, triste verlo tan abajo :-(
masaers
3
@LF Marginalmente mejor. Pero muchas cosas aún no están cubiertas: touppery toloweraún funcionan en caracteres individuales. La clase de cadena aún no tiene una noción de normalización (por ejemplo, si una "ü" está codificada como "u con diaeresis" o "u + diaeresis combinada") o donde una cadena puede o no estar separada. La lista continua. u8string es (como las otras clases de cadena estándar) apropiado para "pasar a través". Pero si desea procesar Unicode, necesita una UCI.
DevSolar
36

Usando el rango para el ciclo de C ++ 11, un código más simple sería:

#include <iostream>       // std::cout
#include <string>         // std::string
#include <locale>         // std::locale, std::tolower

int main ()
{
  std::locale loc;
  std::string str="Test String.\n";

 for(auto elem : str)
    std::cout << std::tolower(elem,loc);
}
incisiones
fuente
99
Sin embargo, en una máquina francesa, este programa no convierte caracteres no ASCII permitidos en el idioma francés. Por ejemplo, una cadena 'Test String123. É Ï \ n 'se convertirá en:' prueba string123. É Ï \ n 'aunque los caracteres É Ï y sus componentes en minúscula' é 'y' ï 'están permitidos en francés. Parece que ninguna solución para eso fue proporcionada por otros mensajes de este hilo.
incide el
Creo que debe establecer una configuración regional adecuada para eso.
user1095108
@incises, entonces alguien publicó una respuesta sobre la UCI y ese es ciertamente el camino a seguir. Más fácil que la mayoría de las otras soluciones que intentarían comprender la configuración regional.
Alexis Wilke
Prefiero no usar bibliotecas externas cuando sea posible, personalmente.
kayleeFrye_onDeck
15

Este es un seguimiento de la respuesta de Stefan Mai: si desea colocar el resultado de la conversión en otra cadena, debe preasignar su espacio de almacenamiento antes de llamar std::transform. Dado que STL almacena los caracteres transformados en el iterador de destino (incrementándolo en cada iteración del bucle), la cadena de destino no se redimensionará automáticamente y corre el riesgo de pisotear la memoria.

#include <string>
#include <algorithm>
#include <iostream>

int main (int argc, char* argv[])
{
  std::string sourceString = "Abc";
  std::string destinationString;

  // Allocate the destination space
  destinationString.resize(sourceString.size());

  // Convert the source string to lower case
  // storing the result in destination string
  std::transform(sourceString.begin(),
                 sourceString.end(),
                 destinationString.begin(),
                 ::tolower);

  // Output the result of the conversion
  std::cout << sourceString
            << " -> "
            << destinationString
            << std::endl;
}
usuario2218467
fuente
1
Esto no
cambió de
También podría usar un iterador de inserción posterior aquí en lugar del cambio de tamaño manual.
chile
11

Otro enfoque que utiliza el rango basado para el bucle con la variable de referencia

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

cout<<test<<endl;
Gilson PJ
fuente
6

Por lo que veo, las bibliotecas de Boost son realmente malas en cuanto al rendimiento. He probado su unordered_map a STL y fue un promedio 3 veces más lento (el mejor caso 2, el peor fue 10 veces). Además, este algoritmo parece demasiado bajo.

La diferencia es tan grande que estoy seguro de que cualquier adición que necesite hacer para tolowerigualar el impulso "para sus necesidades" será mucho más rápido que el impulso.

He realizado estas pruebas en un Amazon EC2, por lo tanto, el rendimiento varió durante la prueba, pero aún se entiende.

./test
Elapsed time: 12365milliseconds
Elapsed time: 1640milliseconds
./test
Elapsed time: 26978milliseconds
Elapsed time: 1646milliseconds
./test
Elapsed time: 6957milliseconds
Elapsed time: 1634milliseconds
./test
Elapsed time: 23177milliseconds
Elapsed time: 2421milliseconds
./test
Elapsed time: 17342milliseconds
Elapsed time: 14132milliseconds
./test
Elapsed time: 7355milliseconds
Elapsed time: 1645milliseconds

-O2 lo hizo así:

./test
Elapsed time: 3769milliseconds
Elapsed time: 565milliseconds
./test
Elapsed time: 3815milliseconds
Elapsed time: 565milliseconds
./test
Elapsed time: 3643milliseconds
Elapsed time: 566milliseconds
./test
Elapsed time: 22018milliseconds
Elapsed time: 566milliseconds
./test
Elapsed time: 3845milliseconds
Elapsed time: 569milliseconds

Fuente:

string str;
bench.start();
for(long long i=0;i<1000000;i++)
{
    str="DSFZKMdskfdsjfsdfJDASFNSDJFXCKVdnjsafnjsdfjdnjasnJDNASFDJDSFSDNJjdsanjfsdnfjJNFSDJFSD";
    boost::algorithm::to_lower(str);
}
bench.end();

bench.start();
for(long long i=0;i<1000000;i++)
{
    str="DSFZKMdskfdsjfsdfJDASFNSDJFXCKVdnjsafnjsdfjdnjasnJDNASFDJDSFSDNJjdsanjfsdnfjJNFSDJFSD";
    for(unsigned short loop=0;loop < str.size();loop++)
    {
        str[loop]=tolower(str[loop]);
    }
}
bench.end();

Supongo que debería hacer las pruebas en una máquina dedicada, pero usaré este EC2, así que realmente no necesito probarlo en mi máquina.

Etherealone
fuente
1
¿Has abierto las opciones de optimización al compilarlo? Estoy pensando que la biblioteca STL heavy boost debería funcionar mejor con un alto nivel de optimización.
Wei Song
1
Usé -O2 en una de las pruebas, y nada más.
Etherealone
2
El rendimiento de unordered_map depende del algoritmo de hash combinado con los datos que está utilizando. No existe un algoritmo mágico de hashing que funcione para todos y todos los datos para hacer que el mapa desordenado sea lo más rápido posible. Benchmark y probar cosas diferentes. La razón por la que está obteniendo un rendimiento peor es porque con el hash que está utilizando, está obteniendo muchas colisiones, lo que básicamente provoca una búsqueda en una lista. Consulte este sitio para obtener más información: fgda.pl/post/7/gcc-hash-map-vs-unordered-map Para mis propósitos, la función proporcionada en el enlace redujo las colisiones y, por lo tanto, fue muy rápida.
leetNightshade
6

La manera más simple de convertir cadenas en minúsculas sin preocuparse por el espacio de nombres estándar es la siguiente

1: cadena con / sin espacios

#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
int main(){
    string str;
    getline(cin,str);
//------------function to convert string into lowercase---------------
    transform(str.begin(), str.end(), str.begin(), ::tolower);
//--------------------------------------------------------------------
    cout<<str;
    return 0;
}

2: cadena sin espacios

#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
int main(){
    string str;
    cin>>str;
//------------function to convert string into lowercase---------------
    transform(str.begin(), str.end(), str.begin(), ::tolower);
//--------------------------------------------------------------------
    cout<<str;
    return 0;
}
Atul Rokade
fuente
5

std::ctype::tolower()de la biblioteca estándar de localización de C ++ lo hará correctamente por usted. Aquí hay un ejemplo extraído de la página de referencia de tolower

#include <locale>
#include <iostream>

int main () {
  std::locale::global(std::locale("en_US.utf8"));
  std::wcout.imbue(std::locale());
  std::wcout << "In US English UTF-8 locale:\n";
  auto& f = std::use_facet<std::ctype<wchar_t>>(std::locale());
  std::wstring str = L"HELLo, wORLD!";
  std::wcout << "Lowercase form of the string '" << str << "' is ";
  f.tolower(&str[0], &str[0] + str.size());
  std::wcout << "'" << str << "'\n";
}
Sameer
fuente
Agradable, siempre y cuando puedas convertir los personajes en su lugar. ¿Qué pasa si su cadena de origen es const? Eso parece hacerlo un poco más desordenado (por ejemplo, no parece que pueda usar f.tolower()), ya que necesita colocar los caracteres en una nueva cadena. ¿Usarías transform()algo así como std::bind1st( std::mem_fun() )para el operador?
quazar
Para una cadena constante, podemos hacer una copia local y luego convertirla en su lugar.
Sameer
Sí, sin embargo, hacer una copia agrega más sobrecarga.
quazar
Puede usar std :: transform con la versión de ctype :: tolower que no toma punteros. Utilice un adaptador de iterador de inserción posterior y ni siquiera tendrá que preocuparse por cambiar el tamaño de su cadena de salida.
chile
Genial, especialmente porque en libstdc ++ 's tolowercon localeparámetro, la llamada implícita a use_facetparece ser un cuello de botella de rendimiento. Uno de mis compañeros de trabajo ha logrado un aumento de velocidad del 100% al reemplazar boost::iequals(que tiene este problema) con una versión donde use_facetsolo se llama una vez fuera del bucle.
Arne Vogel
3

Una alternativa a Boost es POCO (pocoproject.org).

POCO ofrece dos variantes:

  1. La primera variante hace una copia sin alterar la cadena original.
  2. La segunda variante cambia la cadena original en su lugar.
    Las versiones "In Place" siempre tienen "InPlace" en el nombre.

Ambas versiones se muestran a continuación:

#include "Poco/String.h"
using namespace Poco;

std::string hello("Stack Overflow!");

// Copies "STACK OVERFLOW!" into 'newString' without altering 'hello.'
std::string newString(toUpper(hello));

// Changes newString in-place to read "stack overflow!"
toLowerInPlace(newString);
Jason Enochs
fuente
3

Hay una manera de convertir mayúsculas a minúsculas SIN hacer pruebas if , y es bastante sencillo. El uso de la función isupper () / macro de clocale.h debería solucionar los problemas relacionados con su ubicación, pero si no, siempre puede modificar la UtoL [] al contenido de su corazón.

Dado que los caracteres de C son solo entradas de 8 bits (ignorando los juegos de caracteres anchos por el momento), puede crear una matriz de 256 bytes que contenga un conjunto alternativo de caracteres, y en la función de conversión use los caracteres en su cadena como subíndices en el matriz de conversión

Sin embargo, en lugar de una asignación 1 por 1, proporcione a los miembros de la matriz en mayúsculas los valores BYTE int para los caracteres en minúsculas. Puede encontrar islower () e isupper () útiles aquí.

ingrese la descripción de la imagen aquí

El código se ve así ...

#include <clocale>
static char UtoL[256];
// ----------------------------------------------------------------------------
void InitUtoLMap()  {
    for (int i = 0; i < sizeof(UtoL); i++)  {
        if (isupper(i)) {
            UtoL[i] = (char)(i + 32);
        }   else    {
            UtoL[i] = i;
        }
    }
}
// ----------------------------------------------------------------------------
char *LowerStr(char *szMyStr) {
    char *p = szMyStr;
    // do conversion in-place so as not to require a destination buffer
    while (*p) {        // szMyStr must be null-terminated
        *p = UtoL[*p];  
        p++;
    }
    return szMyStr;
}
// ----------------------------------------------------------------------------
int main() {
    time_t start;
    char *Lowered, Upper[128];
    InitUtoLMap();
    strcpy(Upper, "Every GOOD boy does FINE!");

    Lowered = LowerStr(Upper);
    return 0;
}

Este enfoque, al mismo tiempo, le permitirá reasignar cualquier otro personaje que desee cambiar.

Este enfoque tiene una gran ventaja cuando se ejecuta en procesadores modernos, no hay necesidad de hacer predicciones de ramificación ya que no hay pruebas de ramificación. Esto guarda la lógica de predicción de bifurcación de la CPU para otros bucles y tiende a evitar paradas de canalización.

Algunos aquí pueden reconocer este enfoque como el mismo utilizado para convertir EBCDIC a ASCII.

usuario2548100
fuente
2
"Hay una manera de convertir mayúsculas a minúsculas SIN hacer si las pruebas" ¿alguna vez oyeron hablar de las tablas de búsqueda?
Gábor Buella
1
Comportamiento indefinido para caracteres negativos.
Roland Illig
Las CPU modernas tienen cuellos de botella en la memoria, no en la CPU. La evaluación comparativa sería interesante.
Contango
3

Como ninguna de las respuestas mencionó la próxima biblioteca de Rangos, que está disponible en la biblioteca estándar desde C ++ 20, y actualmente está disponible por separado en GitHub como range-v3, me gustaría agregar una forma de realizar esta conversión usándola.

Para modificar la cadena en el lugar:

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

Para generar una nueva cadena:

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

(No olvide #include <cctype>y los encabezados de Rangos requeridos).

Nota: el uso de unsigned charcomo argumento para la lambda está inspirado en cppreference , que establece:

Como todas las demás funciones de <cctype>, el comportamiento de std::tolowerno está definido si el valor del argumento no es representable unsigned charni es igual a EOF. Para usar estas funciones de forma segura con chars (o signed chars) sin formato , el argumento primero debe convertirse a unsigned char:

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

Del mismo modo, no deben usarse directamente con algoritmos estándar cuando el tipo de valor del iterador es charo signed char. En su lugar, convierta el valor a unsigned charprimero:

std::string str_tolower(std::string s) {
    std::transform(s.begin(), s.end(), s.begin(), 
                // static_cast<int(*)(int)>(std::tolower)         // wrong
                // [](int c){ return std::tolower(c); }           // wrong
                // [](char c){ return std::tolower(c); }          // wrong
                   [](unsigned char c){ return std::tolower(c); } // correct
                  );
    return s;
}
LF
fuente
3

Mis propias funciones de plantilla que realizan mayúsculas / minúsculas.

#include <string>
#include <algorithm>

//
//  Lowercases string
//
template <typename T>
std::basic_string<T> lowercase(const std::basic_string<T>& s)
{
    std::basic_string<T> s2 = s;
    std::transform(s2.begin(), s2.end(), s2.begin(), tolower);
    return std::move(s2);
}

//
// Uppercases string
//
template <typename T>
std::basic_string<T> uppercase(const std::basic_string<T>& s)
{
    std::basic_string<T> s2 = s;
    std::transform(s2.begin(), s2.end(), s2.begin(), toupper);
    return std::move(s2);
}
TarmoPikaro
fuente
Esto es lo que necesitaba. Acabo de usar el towlowerpara caracteres anchos que admite el UTF-16.
Juv
2

Aquí hay una técnica de macro si quieres algo simple:

#define STRTOLOWER(x) std::transform (x.begin(), x.end(), x.begin(), ::tolower)
#define STRTOUPPER(x) std::transform (x.begin(), x.end(), x.begin(), ::toupper)
#define STRTOUCFIRST(x) std::transform (x.begin(), x.begin()+1, x.begin(),  ::toupper); std::transform (x.begin()+1, x.end(),   x.begin()+1,::tolower)

Sin embargo, tenga en cuenta que el comentario de @ AndreasSpindler sobre esta respuesta sigue siendo una consideración importante, sin embargo, si está trabajando en algo que no son solo caracteres ASCII.

Volomike
fuente
1
Estoy rechazando esto por dar macros cuando existe una solución perfectamente buena, incluso puedes dar esas soluciones.
Más claro el
2
La técnica de macro significa menos tipeo de código para algo que uno usaría comúnmente en la programación. ¿Por qué no usar eso? De lo contrario, ¿por qué tener macros?
Volomike
3
Las macros son un legado de C en el que se está trabajando duro para deshacerse de ellas. Si desea reducir la cantidad de escritura, use una función o una lambda. void strtoupper(std::string& x) { std::transform (x.begin(), x.end(), x.begin(), ::toupper); }
Más claro el
1
@Clearer Como quiero ser un mejor codificador, ¿puede proporcionarme algún enlace de documentación ANSI donde algún comité ANSI C ++ diga algo como "Necesitamos convocar una reunión para eliminar las macros de C ++"? ¿O alguna otra hoja de ruta?
Volomike
2
No, no puedo Sin embargo, la postura de Bjarne sobre el tema se ha dejado bastante clara en varias ocasiones. Además, hay muchas razones para no usar macros en C ni en C ++. xpodría ser una expresión válida, que solo se compila correctamente pero dará resultados completamente falsos debido a las macros.
Más claro
2
// tolower example (C++)
#include <iostream>       // std::cout
#include <string>         // std::string
#include <locale>         // std::locale, std::tolower

int main ()
{
  std::locale loc;
  std::string str="Test String.\n";
  for (std::string::size_type i=0; i<str.length(); ++i)
    std::cout << std::tolower(str[i],loc);
  return 0;
}

Para más información: http://www.cplusplus.com/reference/locale/tolower/

MoraRockey
fuente
2

¿Existe alguna alternativa que funcione el 100% del tiempo?

No

Hay varias preguntas que debe hacerse antes de elegir un método en minúsculas.

  1. ¿Cómo se codifica la cadena? ASCII simple? UTF-8? alguna forma de codificación heredada ASCII extendida?
  2. ¿Qué quieres decir con minúscula de todos modos? ¡Las reglas de mapeo de casos varían entre idiomas! ¿Desea algo que esté localizado en la configuración regional de los usuarios? ¿Desea algo que se comporte de manera consistente en todos los sistemas en los que se ejecuta su software? ¿Solo quieres minúsculas caracteres ASCII y pasar por todo lo demás?
  3. ¿Qué bibliotecas hay disponibles?

Una vez que tenga respuestas a esas preguntas, puede comenzar a buscar una solución que se adapte a sus necesidades. ¡No hay una talla única que funcione para todos en todas partes!

lavado
fuente
2

Prueba esta función :)

string toLowerCase(string str) {
    int str_len = str.length();
    string final_str = "";
    for(int i=0; i<str_len; i++) {
        char character = str[i];
        if(character>=65 && character<=92) {
            final_str += (character+32);
        } else {
            final_str += character;
        }
    }
    return final_str;
}
BuSaeed
fuente
1

En las plataformas de Microsoft puede usar la strlwrfamilia de funciones: http://msdn.microsoft.com/en-us/library/hkxwh33z.aspx

// crt_strlwr.c
// compile with: /W3
// This program uses _strlwr and _strupr to create
// uppercase and lowercase copies of a mixed-case string.
#include <string.h>
#include <stdio.h>

int main( void )
{
   char string[100] = "The String to End All Strings!";
   char * copy1 = _strdup( string ); // make two copies
   char * copy2 = _strdup( string );

   _strlwr( copy1 ); // C4996
   _strupr( copy2 ); // C4996

   printf( "Mixed: %s\n", string );
   printf( "Lower: %s\n", copy1 );
   printf( "Upper: %s\n", copy2 );

   free( copy1 );
   free( copy2 );
}
Autodidacta
fuente
0

Fragmento de código

#include<bits/stdc++.h>
using namespace std;


int main ()
{
    ios::sync_with_stdio(false);

    string str="String Convert\n";

    for(int i=0; i<str.size(); i++)
    {
      str[i] = tolower(str[i]);
    }
    cout<<str<<endl;

    return 0;
}
rashedcs
fuente
0

Copie porque no se permitió mejorar la respuesta. Gracias


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

Explicación:

for(auto& c : test)es un bucle basado en rango del tipo :
for (range_declaration:range_expression)loop_statement

  1. range_declaration: auto& c
    Aquí se utiliza el especificador automático para la deducción automática de tipo. Entonces el tipo se deduce del inicializador de variables.

  2. range_expression: test
    El rango en este caso son los caracteres de la cadena test.

Los caracteres de la cadena testestán disponibles como referencia dentro del identificador de bucle for c.

goulashsoup
fuente
Por favor, aclare de dónde copió su respuesta.
bfontaine
0

C ++ no tiene implementados los métodos tolower o toupper para string, pero está disponible para char. Uno puede leer fácilmente cada carácter de cadena, convertirlo en el caso requerido y volver a colocarlo en cadena. Un código de muestra sin usar ninguna biblioteca de terceros:

#include<iostream>

int main(){
  std::string str = std::string("How IS The Josh");
  for(char &ch : str){
    ch = std::tolower(ch);
  }
  std::cout<<str<<std::endl;
  return 0;
}

Para operación basada en caracteres en cadena: para cada carácter en cadena

Mahipal
fuente
-1

Esta podría ser otra versión simple para convertir mayúsculas a minúsculas y viceversa. Usé la versión de comunidad VS2017 para compilar este código fuente.

#include <iostream>
#include <string>
using namespace std;

int main()
{
    std::string _input = "lowercasetouppercase";
#if 0
    // My idea is to use the ascii value to convert
    char upperA = 'A';
    char lowerA = 'a';

    cout << (int)upperA << endl; // ASCII value of 'A' -> 65
    cout << (int)lowerA << endl; // ASCII value of 'a' -> 97
    // 97-65 = 32; // Difference of ASCII value of upper and lower a
#endif // 0

    cout << "Input String = " << _input.c_str() << endl;
    for (int i = 0; i < _input.length(); ++i)
    {
        _input[i] -= 32; // To convert lower to upper
#if 0
        _input[i] += 32; // To convert upper to lower
#endif // 0
    }
    cout << "Output String = " << _input.c_str() << endl;

    return 0;
}

Nota: si hay caracteres especiales, entonces deben manejarse utilizando la verificación de condición.

Praveer Kumar
fuente
-8

Intenté std :: transform, todo lo que obtengo es un error abominable de compilación stl criptic que solo los druidas de hace 200 años pueden entender (no se puede convertir en gripe flibidi flabidi)

esto funciona bien y se puede ajustar fácilmente

string LowerCase(string s)
{
    int dif='a'-'A';
    for(int i=0;i<s.length();i++)
    {
        if((s[i]>='A')&&(s[i]<='Z'))
            s[i]+=dif;
    }
   return s;
}

string UpperCase(string s)
{
   int dif='a'-'A';
    for(int i=0;i<s.length();i++)
    {
        if((s[i]>='a')&&(s[i]<='z'))
            s[i]-=dif;
    }
   return s;
}
fdsfdsfdsfds
fuente