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


Quiero convertir un std::string a minúsculas Estoy al tanto de la función tolower()Sin embargo, en el pasado he tenido problemas con esta función y, de todos modos, no es ideal de ninguna manera, ya que se usa con std::string requeriría iterar sobre cada personaje.

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


620
2017-11-24 11:49


origen


Respuestas:


De esta:

#include <algorithm>
#include <string> 

std::string data = "Abc"; 
std::transform(data.begin(), data.end(), data.begin(), ::tolower);

Realmente no vas a escaparte con la iteración de cada personaje. No hay manera de saber si el carácter es en minúsculas o en mayúsculas de lo contrario.

Si realmente odias tolower(), aquí hay una alternativa no portátil que no recomiendo que uses:

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

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

Sé consciente de ::tolower() solo puede hacer una sustitución de caracteres por byte único, que no se ajusta bien a muchos scripts, especialmente si se usa una codificación de múltiples bytes como UTF-8.


749
2017-11-24 11:59



Hay un algoritmo de cadena Boost 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);

281
2017-11-24 11:57



tl; dr

Utilizar el Biblioteca de ICU.


Primero debes 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? ¿Sabe lo que está usando para convertir mayúsculas a minúsculas? (O falla miserablemente para los personajes 0x7f?)

Si está utilizando UTF-8 (la única opción correcta entre las codificaciones de 8 bits) con std::string como 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 tiene conocimiento del concepto multibyte. Incluso algo tan simple como .substr() es una bomba de tiempo tictac (Debido a que dividir una secuencia multibyte resultará en una (sub) cadena no válida).

Y tan pronto como intentes algo así como std::toupper( 'ß' ), en alguna codificación, estás en un problema profundo. (Porque simplemente no es posible hacer esto "bien" con la biblioteca estándar, que solo puede entregar uno personaje resultante, no el "SS" es necesario aquí.) [1] Otro ejemplo sería std::tolower( 'I' ), que debería arrojar resultados diferentes dependiendo de la localidad. En Alemania, 'i' sería correcto; en Turquía, 'ı' (LETRA PEQUEÑA MINÚSCULA DOTLESS I) es el resultado esperado.

Luego está el punto de que la biblioteca estándar depende de las configuraciones regionales soportado en la máquina se está ejecutando su software ... y ¿qué hace si no es así?

Entonces, ¿qué eres? De Verdad buscando es una clase de cuerda que es capaz de manejar todo esto correctamente, y eso es no  std::string.

(C ++ 11 nota: std::u16string y std::u32string son mejor, pero aún no es perfecto.)

Mientras Boost miradas Bueno, API sabio, Boost.Locale es básicamente un envoltorio alrededor UCI. Si Boost es compilado con soporte de ICU ... si no es así, Boost.Locale está limitado al soporte de localización compilado para la biblioteca estándar.

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

Personalmente, recomendaría recibir soporte completo de Unicode directamente de 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()
{
    char const * someString = "Eidenges\xe4\xdf";
    icu::UnicodeString someUString( someString, "ISO-8859-1" );
    // Setting the locale explicitly here for completeness.
    // Usually you would use the user-specified system locale.
    std::cout << someUString.toLower( "de_DE" ) << "\n";
    std::cout << someUString.toUpper( "de_DE" ) << "\n";
    return 0;
}

Compile (con G ++ en este ejemplo):

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

Esto da:

eidengesäß
EIDENGESÄSS

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


167
2018-06-05 15:06



Si la cadena contiene caracteres UTF-8 fuera del rango ASCII, boost :: algorithm :: to_lower no los convertirá. Mejor uso 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


27
2017-10-10 07:24



Usando el bucle for de C ++ 11 basado en rango, 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);
}

22
2017-10-09 08:00



Esta es una continuación 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. Como STL almacena caracteres transformados en el iterador de destino (incrementándolos en cada iteración del ciclo), la cadena de destino no se redimensionará automáticamente, y corre el riesgo de que la memoria se acelere.

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

14
2018-03-28 06:25



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

La diferencia es tan grande que estoy seguro de que cualquier adición que necesites hacer a tolower para que sea igual a impulsar "para sus necesidades" será mucho más rápido que impulsar.

He hecho estas pruebas en Amazon EC2, por lo tanto, el rendimiento varió durante la prueba, pero aún así 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é esta EC2, así que realmente no necesito probarla en mi máquina.


7
2017-08-04 20:01