Pregunta

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 es casi ideal de todos modos, como cuando se utiliza un std::string requeriría de iterar sobre cada personaje.

Hay una alternativa que funciona el 100% del tiempo?

¿Fue útil?

Solución

Adaptado de No Preguntas 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 escaparte sin recorrer cada personaje. No hay forma de saber si el carácter es minúscula o mayúscula de lo contrario.

Si realmente odias tolower() , aquí hay un especialista Alternativa solo ASCII que no te 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 <=> solo puede hacer 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.

Otros consejos

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, por no lugar :

#include <boost/algorithm/string.hpp>

const std::string str = "HELLO, WORLD!";
const std::string lower_str = boost::algorithm::to_lower_copy(str);

tl;dr

El uso de la La UCI de la biblioteca.


Primero usted tiene que responder a una pregunta:¿Cuál es la codificación de su std::string?Es ISO-8859-1?O quizás ISO-8859-8?O Windows De La Página De Códigos 1252? ¿Lo que usted está utilizando para convertir superior a minúsculas saben que? (O lo hace fallar miserablemente para los caracteres más 0x7f?)

Si usted está usando UTF-8 (la única elección sensata entre los 8-bits codificaciones) con std::string como contenedor, ya estás engañando a ti mismo en la creencia de que usted todavía está en control de las cosas, porque usted está guardando una secuencia de caracteres multibyte en un contenedor que no es consciente de la multibyte concepto.Incluso algo tan simple como .substr() es un tictac bombas de tiempo.(Debido a que la división de una secuencia multibyte resultará en un inválido (sub-) de la cadena.)

Y tan pronto como se intenta algo como std::toupper( 'ß' ), en cualquier codificación, estás en serios problemas.(Porque simplemente no es posible hacerlo "bien" con la biblioteca estándar, que sólo puede ofrecer uno resultado personaje, no el "SS" se necesita aquí.) [1] Otro ejemplo sería std::tolower( 'I' ), lo cual debería producir resultados diferentes dependiendo de la configuración regional.En Alemania, 'i' sería correcto;en Turquía, 'ı' (LETRA a minúscula LATINA sin punto I) es el resultado esperado (que, de nuevo, es más de un byte en la codificación UTF-8).

Luego está el punto de que la biblioteca estándar es dependiendo de la localización compatible en el equipo el software se está ejecutando en...y, ¿qué hacer si no?

Así que lo que están realmente buscando es una clase string que es capaz de lidiar con todo esto correctamente y que es no std::string.

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

Mientras Boost se ve agradable, API sabio, Boost.Configuración regional es básicamente un contenedor de La UCI. Si Boost es compilado con UCI apoyo...si no lo es, el Impulso.Configuración regional se limita a la configuración regional de apoyo compilado para la biblioteca estándar.

Y créanme, llegar Impulso a compilar con UCI puede ser un verdadero dolor de cabeza a veces.(No hay binarios precompilados para Windows, de manera que tendría para el suministro de los mismos, junto con su solicitud, y que se abre toda una nueva lata de gusanos...)

Así que personalmente yo recomendaría conseguir soporte completo de Unicode directamente de la boca del caballo y el uso de la La 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;
}

Compilar (con G++ en este ejemplo):

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

Esto nos da:

eidengesäß
EIDENGESÄSS

[1] En el año 2017, el Consejo de la Ortografía alemana decidió que la "ß" U+1E9E LATINA mayúscula SHARP S podría ser utilizado oficialmente, como una opción al lado de la tradicional "SS" conversión para evitar la ambigüedad por ejemplo,en los pasaportes (donde los nombres en mayúsculas).Mi hermosa ir a ejemplo, obsoleta por la decisión de la comisión...

Si la cadena contiene caracteres UTF-8 fuera del rango ASCII, entonces boost :: algoritm :: to_lower no los convertirá. Mejor use boost :: locale :: to_lower cuando UTF-8 esté involucrado. Ver http://www.boost.org/doc/libs/1_51_0 /libs/locale/doc/html/conversions.html

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

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

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

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

cout<<test<<endl;

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 cualquier adición que tendrá que hacer a tolower para que sea igual a impulsar " para sus necesidades " será mucho más rápido que impulsar.

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

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

La forma más sencilla 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;
}

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

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.
    " In Place " las versiones 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);

Hay una forma 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 la 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. Aquí puede encontrar islower () e isupper () .

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.

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.

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

  

¿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? ¿Quieres algo que se comporte de manera consistente en todos los sistemas en los que se ejecuta tu software? ¿Solo quieres minúsculas caracteres ASCII y pasar por todo lo demás?
  3. ¿Qué bibliotecas están 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!

Ya que ninguna de las respuestas se mencionó la próxima Rangos de la biblioteca, que está disponible en la biblioteca estándar, ya que C++20, y actualmente disponible por separado en GitHub como range-v3, Me gustaría añadir una forma de realizar esta conversión de uso de la misma.

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 la exigencia de los Rangos de los encabezados.)

Nota:el uso de unsigned char como el argumento de la lambda es inspirada por cppreference, que establece que:

Como todas las otras funciones de <cctype>, el comportamiento de los std::tolower no está definido si el argumento del valor no es representable como unsigned char ni igual a EOF.Para utilizar estas funciones de manera segura con la llanura chars (o signed chars), el argumento debe ser convertida a unsigned char:

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

Asimismo, no debe ser utilizado directamente con algoritmos estándar cuando el iterador del tipo de valor es char o signed char.En su lugar, convertir el valor a unsigned char primero:

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

En las plataformas de Microsoft puede usar la familia de funciones strlwr: 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 );
}

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

Use fplus :: to_lower_case ().

(fplus: https://github.com/Dobiasd/FunctionalPlus .

Busque 'to_lower_case' en http://www.editgym.com/fplus-api -search / )

fplus::to_lower_case(std::string("ABC")) == std::string("abc");

Copiar porque no se permitió mejorar la respuesta. Gracias SO


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

Explicación :

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

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

  2. c : <=>
    El rango en este caso son los caracteres de la cadena <=>.

Los caracteres de la cadena <=> están disponibles como referencia dentro del bucle for a través del identificador <=>.

C ++ no tiene implementados 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 la operación basada en caracteres en la cadena: Para cada carácter en la cadena

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

Esta podría ser otra versión simple para convertir mayúsculas a minúsculas y viceversa. Utilicé 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.

Intenté std :: transform, todo lo que obtengo es un error abominable de compilación de 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;
}
//You can really just write one on the fly whenever you need one.
#include <string>
void _lower_case(std::string& s){
for(unsigned short l = s.size();l;s[--l]|=(1<<5));
}
//Here is an example.
//http://ideone.com/mw2eDK
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top