Cómo convertir una instancia de std::string a minúsculas

11 minutos de lectura

Como convertir una instancia de stdstring a minusculas
Konrad

quiero convertir un std::string a minúsculas. 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 ideal, ya que se usa con un std::string requeriría iterar sobre cada carácter.

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

  • ¿De qué otra manera convertiría cada elemento de una lista de cualquier cosa 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 manera de evitar eso.

    usuario21037

    24 de noviembre de 2008 a las 12: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 además de tolower(), toupper(), etc.

    – Konrad

    24 de noviembre de 2008 a las 12:24

  • Si tiene una matriz de caracteres de estilo C, entonces supongo que puede agregar ox20202020 a cada bloque de 4 caracteres (siempre que TODOS estén en mayúsculas) para convertir 4 caracteres a minúsculas a la vez.

    usuario21037

    24 de noviembre de 2008 a las 13:05

  • @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 que probablemente son tontas que casi nunca valen la pena…

    –Steve Jessop

    24 de noviembre de 2008 a las 13:11

  • No sé por qué habría sido rechazado… ciertamente está redactado un poco extraño (porque tienes que iterar a través de cada elemento de alguna manera), pero es una pregunta válida

    – madriguera

    24 de noviembre de 2008 a las 13:19

Como convertir una instancia de stdstring a minusculas
stefano mai

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 salirte con la tuya sin iterar a través de cada personaje. De lo contrario, no hay forma de saber si el carácter está en minúsculas o en mayúsculas.

si realmente odias tolower()aquí hay una alternativa especializada solo en ASCII que no recomiendo usar:

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

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

Sé consciente de tolower() solo puede hacer una sustitución de carácter por byte único, lo que no se ajusta a muchos scripts, especialmente si se usa una codificación de varios bytes como UTF-8.

  • (Puede ser antiguo, los algoritmos en cuestión han cambiado poco) @Stefan Mai: ¿Qué tipo de “gran sobrecarga” hay al llamar a los algoritmos STL? Las funciones son bastante sencillas (es decir, simples bucles for) 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-

    11 de noviembre de 2011 a las 22:14

  • Cada vez que asumes que los caracteres son ASCII, Dios mata a un gatito. 🙁

    – Trapo

    10 de febrero de 2014 a las 20:49

  • Su primer ejemplo potencialmente tiene comportamiento indefinido (paso char para ::tolower(int).) Debe asegurarse de no pasar un valor negativo.

    – juanchopanza

    29 mayo 2014 a las 17:30


  • -1 este uso de ::tolower bien puede fallar, es UB para entrada no ASCII.

    – Saludos y hth. – alf

    29 mayo 2014 a las 17:34

  • Se necesita :: antes de tolower para indicar que se encuentra 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 ::.

    – Carlos Ofria

    30 de julio de 2016 a las 16:43

1647532392 384 Como convertir una instancia de stdstring a minusculas
Robar

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

  • Falla para no ASCII-7.

    – DevSolar

    27 de febrero de 2015 a las 9:28

  • Esto es bastante lento, vea este punto de referencia: godbolt.org/z/neM5jsva1

    – pingüino prehistórico

    29 de junio de 2021 a las 10:31


  • @prehistoricpenguin lento? Bueno, lento es depurar el código porque su propia implementación tiene un error porque era más complicado que simplemente llamar a la biblioteca boost;) Si el código es crítico, como llamado mucho y proporciona un cuello de botella, entonces, bueno, puede ser vale la pena pensar en la lentitud

    – Mayou36

    12 de febrero a las 12:00

1647532392 12 Como convertir una instancia de stdstring a minusculas
DevSolar

tl; dr

Utilizar el biblioteca de la UCI. Si no lo hace, su rutina de conversión se interrumpirá silenciosamente en casos que probablemente ni siquiera sepa que existen.


Primero tienes que responder una pregunta: ¿Cuál es el 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és usando para convertir mayúsculas a minúsculas lo sabe? (¿O falla miserablemente para los personajes de más de 0x7f?)

Si está utilizando UTF-8 (la única opción sensata entre las codificaciones de 8 bits) con std::string como contenedor, ya te estás engañando a ti mismo si crees que todavía tienes el control de las cosas. Está almacenando una secuencia de caracteres de varios bytes en un contenedor que no conoce el concepto de multibyte, ¡y tampoco la mayoría de las operaciones que puede realizar en él! Incluso algo tan simple como .substr() podría dar como resultado cadenas (sub) no válidas porque se dividió en medio de una secuencia de varios bytes.

Tan pronto como intentas algo como std::toupper( 'ß' )o std::tolower( 'Σ' ) en ninguna codificación, estás en problemas. Porque 1), el estándar solo opera en un carácter a la vez, por lo que simplemente no puede cambiar ß dentro SS como seria correcto. Y 2), el estándar solo opera en un carácter a la vez, por lo que no puede decidir si Σ está en medio de una palabra (donde σ sería correcto), o al final (ς). Otro ejemplo sería std::tolower( 'I' )que debería arrojar resultados diferentes dependiendo de la localidad — prácticamente en todas partes que usted esperaría ipero en Turquía ı (LETRA MINÚSCULA LATINA SIN PUNTO I) es la respuesta correcta (que, nuevamente, es más de un byte en codificación UTF-8).

Entonces, ninguna conversión de mayúsculas y minúsculas que funciona en un carácter a la vez, o peor aún, un byte a la vez, está roto por diseño. Esto incluye todos los std:: variantes existentes en este momento.

Luego está el punto de que la biblioteca estándar, por lo que es capaz de hacer, depende de qué lugares sean soportado en la máquina en la que se ejecuta su software… y ¿qué hace si su configuración regional de destino se encuentra entre las que no son compatibles con la máquina de su cliente?

entonces que eres De Verdad looking for es una clase de cadena que es capaz de manejar todo esto correctamente, y eso es no cualquiera de los std::basic_string<> variantes.

(C++11 nota: std::u16string y std::u32string están mejor, pero aún no es perfecto. C ++ 20 traído std::u8stringpero todo lo que hacen es especificar el codificación. En muchos otros aspectos, aún ignoran la mecánica de Unicode, como la normalización, la intercalación, …)

Mientras aumenta mira bueno, en cuanto a la API, Boost.Locale es básicamente un envoltorio UCI. Si impulso es compilado con compatibilidad con ICU… si no lo es, Boost.Locale se limita a la compatibilidad con la configuración regional compilada para la biblioteca estándar.

Y créeme, consiguiendo Boost para compilar con ICU puede ser un verdadero dolor a veces. (No hay archivos binarios precompilados para Windows que incluyan ICU, por lo que deberá suministrarlos junto con su aplicación y que abre una nueva lata de gusanos…)

Así que, personalmente, recomendaría obtener compatibilidad total con Unicode directamente desde la boca del caballo y usar el UCI biblioteca 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. No <algorithm>La solución basada en puede darte eso.

  • 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 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 realizar su propio manejo de Unicode. Si tiene que lidiar con texto real, use ICU. Gracias, @DevSolar

    – lmat – Reincorporar a Mónica

    25/03/2015 a las 14:00

  • ¿ICU está disponible de forma predeterminada en Ubuntu/Windows o debe instalarse por separado? Además, ¿qué tal esta respuesta: stackoverflow.com/a/35075839/207661?

    – Shital Shah

    11 mayo 2016 a las 19:00


  • icu::UnicodeString::length() técnicamente también le está mintiendo (aunque con menos frecuencia), ya que informa la cantidad de unidades de código de 16 bits en lugar de la cantidad de puntos de código. 😉

    – masajistas

    15 de junio de 2017 a las 2:17

  • @masaers: para ser completamente justos, con cosas como combinar caracteres, ensambladores de ancho cero y marcadores de derecha a izquierda, la cantidad de puntos de código no tiene sentido. Eliminaré ese comentario.

    – DevSolar

    15 de junio de 2017 a las 5:26

  • @DevSolar ¡De acuerdo! El concepto de longitud no tiene sentido en el texto (podríamos agregar ligaduras a la lista de infractores). Dicho esto, dado que las personas están acostumbradas a que las pestañas y los caracteres de control ocupen una unidad de longitud, los puntos de código serían la medida más intuitiva. Ah, y gracias por dar la respuesta correcta, es triste verlo tan abajo 🙁

    – masajistas

    15 de junio de 2017 a las 6:51

Usando el bucle for basado en rango 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);
}

Si la cadena contiene caracteres UTF-8 fuera del rango ASCII, boost::algorithm::to_lower no los convertirá. Es mejor usar boost::locale::to_lower cuando se trata de UTF-8. Ver http://www.boost.org/doc/libs/1_51_0/libs/locale/doc/html/conversions.html

  • ¿Un ejemplo de trabajo?

    – Velkan

    2 ene a las 15:43

1647532393 526 Como convertir una instancia de stdstring a minusculas
gilson pj

Otro enfoque que utiliza el bucle for basado en rango con variable de referencia

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

cout<<test<<endl;

  • ¿Un ejemplo de trabajo?

    – Velkan

    2 ene a las 15:43

1647532393 280 Como convertir una instancia de stdstring a minusculas
usuario2218467

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 ciclo), la cadena de destino no se redimensionará automáticamente y corre el riesgo de pisar 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;
}

  • Esto no cambió el tamaño de Ä en ä para mí

    – Purefan

    23 de enero de 2016 a las 16:12

  • También podría usar un iterador de inserción posterior aquí en lugar de un cambio de tamaño manual.

    – chile

    24 de abril de 2017 a las 1:57

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad