Comparación de cadenas sin distinción entre mayúsculas y minúsculas en C ++ [cerrado]


373

¿Cuál es la mejor manera de hacer una comparación de cadenas sin distinción entre mayúsculas y minúsculas en C ++ sin transformar una cadena en mayúsculas o minúsculas?

Indique si los métodos son compatibles con Unicode y qué tan portátiles son.


@ [Adam] (# 11679): Si bien esta variante es buena en términos de usabilidad, es mala en términos de rendimiento porque crea copias innecesarias. Podría pasar por alto algo, pero creo que la mejor manera (no Unicode) es usarlo std::stricmp. De lo contrario, lea lo que Herb tiene que decir .
Konrad Rudolph el

En c, uno generalmente se vio forzado a tocar toda la cuerda y luego comparar de esa manera, o hacer su propia comparación: P
Michael Dorgan

una pregunta posterior tiene una respuesta más simple: strcasecmp (al menos para los compiladores BSD y POSIX) stackoverflow.com/questions/9182912/…
Móż

@ Mσᶎ esta pregunta también tiene esa respuesta, con la advertencia importante que strcasecmpno es parte del estándar y que falta en al menos un compilador común.
Mark Ransom

Respuestas:


318

Boost incluye un algoritmo útil para esto:

#include <boost/algorithm/string.hpp>
// Or, for fewer header dependencies:
//#include <boost/algorithm/string/predicate.hpp>

std::string str1 = "hello, world!";
std::string str2 = "HELLO, WORLD!";

if (boost::iequals(str1, str2))
{
    // Strings are identical
}

14
¿Es este UTF-8 amigable? Yo creo que no.
vladr

18
No, porque UTF-8 permite cadenas idénticas a ser codificados con diferentes códigos binarios, debido a los acentos, cosechadoras, problemas bidi, etc.
vy32

10
@ vy32 ¡Eso es absolutamente incorrecto! Las combinaciones UTF-8 son mutuamente excluyentes. Siempre debe usar la representación más corta posible, si no lo hace, es una secuencia UTF-8 malformada o un punto de código que debe tratarse con cuidado.
Wiz

48
@Wiz, está ignorando el problema de la normalización de cadenas Unicode. ñ puede representarse como una combinación ˜ seguida de una n, o con un carácter ñ. Debe usar la normalización de cadena Unicode antes de realizar la comparación. Por favor revise el Informe Técnico de Unicode # 15, unicode.org/reports/tr15
vy32

12
@wonkorealtime: porque "ß" convertido a mayúsculas es "SS": fileformat.info/info/unicode/char/df/index.htm
Mooing Duck

118

Aprovecha el estándar char_traits. Recordemos que una std::stringes en realidad un typedef para std::basic_string<char>, o más explícitamente, std::basic_string<char, std::char_traits<char> >. El char_traitstipo describe cómo se comparan los personajes, cómo se copian, cómo se emiten, etc. Todo lo que necesita hacer es escribir una nueva cadena basic_string, y proporcionarle su propia costumbre char_traitsque compare mayúsculas y minúsculas.

struct ci_char_traits : public char_traits<char> {
    static bool eq(char c1, char c2) { return toupper(c1) == toupper(c2); }
    static bool ne(char c1, char c2) { return toupper(c1) != toupper(c2); }
    static bool lt(char c1, char c2) { return toupper(c1) <  toupper(c2); }
    static int compare(const char* s1, const char* s2, size_t n) {
        while( n-- != 0 ) {
            if( toupper(*s1) < toupper(*s2) ) return -1;
            if( toupper(*s1) > toupper(*s2) ) return 1;
            ++s1; ++s2;
        }
        return 0;
    }
    static const char* find(const char* s, int n, char a) {
        while( n-- > 0 && toupper(*s) != toupper(a) ) {
            ++s;
        }
        return s;
    }
};

typedef std::basic_string<char, ci_char_traits> ci_string;

Los detalles están en el Gurú de la semana número 29 .


10
Hasta donde sé por mi propia experimentación, esto hace que su nuevo tipo de cadena sea incompatible con std :: string.
Zan Lynx

8
Por supuesto que sí, por su propio bien. Una cadena que no distingue entre mayúsculas y minúsculas es otra cosa: typedef std::basic_string<char, ci_char_traits<char> > istringno typedef std::basic_string<char, std::char_traits<char> > string.
Andreas Spindler

232
"Todo lo que necesitas hacer ..."
Tim MB

3
@Nathan probablemente use un compilador que pueda realizar CSE básico en el código ...
The Paramagnetic Croissant

17
Cualquier construcción de lenguaje que fuerce tal locura en este caso trivial debería y puede ser abandonada sin remordimientos.
Erik Aronesty

86

El problema con el impulso es que debe vincularse y depender del impulso. No es fácil en algunos casos (por ejemplo, Android).

Y usar char_traits significa que todas sus comparaciones no distinguen entre mayúsculas y minúsculas, lo que generalmente no es lo que desea.

Esto debería ser suficiente. Debería ser razonablemente eficiente. Sin embargo, no maneja unicode ni nada.

bool iequals(const string& a, const string& b)
{
    unsigned int sz = a.size();
    if (b.size() != sz)
        return false;
    for (unsigned int i = 0; i < sz; ++i)
        if (tolower(a[i]) != tolower(b[i]))
            return false;
    return true;
}

Actualización: Bonus C ++ 14 versión ( #include <algorithm>):

bool iequals(const string& a, const string& b)
{
    return std::equal(a.begin(), a.end(),
                      b.begin(), b.end(),
                      [](char a, char b) {
                          return tolower(a) == tolower(b);
                      });
}

27
En realidad, la biblioteca de cadenas de impulso es una biblioteca de solo encabezado, por lo que no es necesario vincular a nada. Además, puede usar la utilidad 'bcp' de boost para copiar solo los encabezados de cadena en su árbol de origen, por lo que no necesita requerir la biblioteca de impulso completa.
Gretchen

Ah, no sabía sobre bcp, parece realmente útil. Gracias por la info!
Timmmm

99
Es bueno saber una versión simple y sin dependencia de impulso.
Deqing

2
@Anna La biblioteca de texto de impulso necesita ser construida y enlazada. Utiliza IBM ICU.
Behrouz.M

También disponible con C ++ 11
marciano

58

Si está en un sistema POSIX, puede usar strcasecmp . Sin embargo, esta función no forma parte del estándar C ni está disponible en Windows. Esto realizará una comparación que no distingue entre mayúsculas y minúsculas en caracteres de 8 bits, siempre que el entorno local sea POSIX. Si la configuración regional no es POSIX, los resultados no están definidos (por lo que podría hacer una comparación localizada o no). Un equivalente de caracteres anchos no está disponible.

De lo contrario, una gran cantidad de implementaciones históricas de la biblioteca C tienen las funciones stricmp () y strnicmp (). Visual C ++ en Windows los renombró a todos al ponerles un guión bajo porque no forman parte del estándar ANSI, por lo que en ese sistema se llaman _stricmp o _strnicmp . Algunas bibliotecas también pueden tener funciones equivalentes de caracteres anchos o multibyte (normalmente denominadas, por ejemplo, wcsicmp, mbcsicmp, etc.).

C y C ++ ignoran en gran medida los problemas de internacionalización, por lo que no hay una buena solución para este problema, excepto el uso de una biblioteca de terceros. Consulte IBM ICU (Componentes internacionales para Unicode) si necesita una biblioteca robusta para C / C ++. ICU es para sistemas Windows y Unix.


53

¿Estás hablando de una comparación insensible a mayúsculas y minúsculas o una comparación Unicode completamente normalizada?

Una comparación tonta no encontrará cadenas que puedan ser iguales pero que no sean binarias iguales.

Ejemplo:

U212B (ANGSTROM SIGN)
U0041 (LATIN CAPITAL LETTER A) + U030A (COMBINING RING ABOVE)
U00C5 (LATIN CAPITAL LETTER A WITH RING ABOVE).

Todos son equivalentes pero también tienen diferentes representaciones binarias.

Dicho esto, la normalización Unicode debería ser una lectura obligatoria, especialmente si planea admitir Hangul, tailandés y otros idiomas asiáticos.

Además, IBM prácticamente patentó los algoritmos Unicode más optimizados y los puso a disposición del público. También mantienen una implementación: IBM ICU


2
Es posible que desee editar ese enlace de la UCI a site.icu-project.org
DevSolar

31

boost :: iequals no es compatible con utf-8 en el caso de una cadena. Puede usar boost :: locale .

comparator<char,collator_base::secondary> cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;
  • Principal: ignora los acentos y las mayúsculas y minúsculas, solo compara letras de base. Por ejemplo, "fachada" y "Fachada" son lo mismo.
  • Secundario: ignore las mayúsculas y minúsculas, pero tenga en cuenta los acentos. "fachada" y "fachada" son diferentes pero "Fachada" y "fachada" son iguales.
  • Terciario: considere tanto el caso como los acentos: "Fachada" y "fachada" son diferentes. Ignorar la puntuación.
  • Cuaternario: considere todos los casos, acentos y puntuación. Las palabras deben ser idénticas en términos de representación Unicode.
  • Idéntico: como cuaternario, pero también compara puntos de código.

30

Mi primer pensamiento para una versión no Unicode fue hacer algo como esto:


bool caseInsensitiveStringCompare(const string& str1, const string& str2) {
    if (str1.size() != str2.size()) {
        return false;
    }
    for (string::const_iterator c1 = str1.begin(), c2 = str2.begin(); c1 != str1.end(); ++c1, ++c2) {
        if (tolower(*c1) != tolower(*c2)) {
            return false;
        }
    }
    return true;
}

20

Puede usar strcasecmpen Unix o stricmpen Windows.

Una cosa que no se ha mencionado hasta ahora es que si está utilizando cadenas stl con estos métodos, es útil comparar primero la longitud de las dos cadenas, ya que esta información ya está disponible en la clase de cadena. Esto podría evitar hacer la costosa comparación de cadenas si las dos cadenas que está comparando ni siquiera tienen la misma longitud en primer lugar.


Dado que determinar la longitud de una cadena consiste en iterar sobre cada carácter de la cadena y compararlo con 0, ¿existe realmente tanta diferencia entre eso y solo comparar las cadenas de inmediato? Supongo que obtienes una mejor ubicación de memoria en el caso en que ambas cadenas no coinciden, pero probablemente casi 2 veces el tiempo de ejecución en caso de una coincidencia.
uliwitness

3
C ++ 11 especifica que la complejidad de std :: string :: length debe ser constante: cplusplus.com/reference/string/string/length
bradtgmurray

1
Es un hecho pequeño y divertido, pero tiene poco que ver aquí. strcasecmp () y stricmp () toman cadenas C sin decorar, por lo que no hay std :: string involucrado.
uliwitness

3
Estos métodos devolverán -1 si compara "a" vs "ab". Las longitudes son diferentes pero "a" viene antes que "ab". Entonces, simplemente comparar las longitudes no es factible si la persona que llama se preocupa por ordenar.
Nathan


13

Estoy tratando de improvisar una buena respuesta de todas las publicaciones, así que ayúdame a editar esto:

Aquí hay un método para hacerlo, aunque transforma las cadenas y no es compatible con Unicode, debería ser portátil, lo cual es una ventaja:

bool caseInsensitiveStringCompare( const std::string& str1, const std::string& str2 ) {
    std::string str1Cpy( str1 );
    std::string str2Cpy( str2 );
    std::transform( str1Cpy.begin(), str1Cpy.end(), str1Cpy.begin(), ::tolower );
    std::transform( str2Cpy.begin(), str2Cpy.end(), str2Cpy.begin(), ::tolower );
    return ( str1Cpy == str2Cpy );
}

Por lo que he leído, esto es más portátil que stricmp () porque stricmp () no es, de hecho, parte de la biblioteca estándar, sino que solo es implementado por la mayoría de los proveedores de compiladores.

Para obtener una implementación verdaderamente amigable con Unicode, parece que debe salir de la biblioteca estándar. Una buena biblioteca de terceros es IBM ICU (Componentes internacionales para Unicode)

También boost :: iequals proporciona una utilidad bastante buena para hacer este tipo de comparación.


¿puede decir qué significa :: tolower, por qué puede usar tolower en lugar de tolower () y qué es '::' antes? gracias
VextoR

17
Esta no es una solución muy eficiente: realiza copias de ambas cadenas y las transforma todas, incluso si el primer carácter es diferente.
Timmmm

2
Si va a hacer una copia de todos modos, ¿por qué no pasar por valor en lugar de por referencia?
celticminstrel

Creo que es simple consejo sin impulso. :)
cmcromance

1
la pregunta pide explícitamente no transformtoda la cadena antes de la comparación
Sandburg

12
str1.size() == str2.size() && std::equal(str1.begin(), str1.end(), str2.begin(), [](auto a, auto b){return std::tolower(a)==std::tolower(b);})

Puede usar el código anterior en C ++ 14 si no está en condiciones de usar boost. Tienes que usar std::towlowerpara caracteres anchos.


44
Creo que debe agregar una str1.size() == str2.size() &&al frente para que no se salga de los límites cuando str2 es un prefijo de str1.
uroeuroburɳ

11

La biblioteca Boost.String tiene muchos algoritmos para hacer comparaciones que no distinguen entre mayúsculas y minúsculas, etc.

Podrías implementar el tuyo, pero ¿por qué molestarse cuando ya está hecho?


1
¿No hay una forma integrada con std :: string?
WilliamKF

66
No, no hay
Dean Harding

3
"... ¿por qué molestarse cuando ya está hecho?" - ¿Qué pasa si no estás usando Boost? El OP no tenía la etiqueta con la pregunta.
jww

11

Para su información, strcmp()y stricmp()son vulnerables al desbordamiento del búfer, ya que solo se procesan hasta que llegan a un terminador nulo. Es más seguro de usar _strncmp()y _strnicmp().


66
Es cierto, aunque sobre-LEER un buffer es significativamente menos peligroso que sobreescribir un buffer.
Adam Rosenfield

44
stricmp()y strnicmp()no es parte del estándar POSIX :-( Sin embargo, usted puede encontrar strcasecmp(), strcasecmp_l(), strncasecmp()y strncasecmp_l()en la cabecera POSIX strings.h:-) ve opengroup.org
olibre

2
@AdamRosenfield 'peor' depende del contexto. En seguridad, a veces el objetivo de una sobrescritura es llegar a leer demasiado.
karmakaze

10

Ver std::lexicographical_compare:

// lexicographical_compare example
#include <iostream>  // std::cout, std::boolalpha
#include <algorithm>  // std::lexicographical_compare
#include <cctype>  // std::tolower

// a case-insensitive comparison function:
bool mycomp (char c1, char c2) {
    return std::tolower(c1) < std::tolower(c2);
}

int main () {
    char foo[] = "Apple";
    char bar[] = "apartment";

    std::cout << std::boolalpha;

    std::cout << "Comparing foo and bar lexicographically (foo < bar):\n";

    std::cout << "Using default comparison (operator<): ";
    std::cout << std::lexicographical_compare(foo, foo + 5, bar, bar + 9);
    std::cout << '\n';

    std::cout << "Using mycomp as comparison object: ";
    std::cout << std::lexicographical_compare(foo, foo + 5, bar, bar + 9, mycomp);
    std::cout << '\n';

    return 0;
}

Manifestación


1
Este método es potencialmente inseguro y no portátil. std::tolowerfunciona solo si el carácter está codificado en ASCII. No existe tal garantía, por std::stringlo que puede ser un comportamiento indefinido fácilmente.
plasmacel

@plasmacel Luego use una función que funcione con otras codificaciones.
Brian Rodriguez

9

Para mis necesidades básicas de comparación de cadenas que no distinguen entre mayúsculas y minúsculas, prefiero no tener que usar una biblioteca externa, ni quiero una clase de cadena separada con rasgos que no distingan entre mayúsculas y minúsculas que sea incompatible con todas mis otras cadenas.

Entonces, lo que se me ocurrió es esto:

bool icasecmp(const string& l, const string& r)
{
    return l.size() == r.size()
        && equal(l.cbegin(), l.cend(), r.cbegin(),
            [](string::value_type l1, string::value_type r1)
                { return toupper(l1) == toupper(r1); });
}

bool icasecmp(const wstring& l, const wstring& r)
{
    return l.size() == r.size()
        && equal(l.cbegin(), l.cend(), r.cbegin(),
            [](wstring::value_type l1, wstring::value_type r1)
                { return towupper(l1) == towupper(r1); });
}

Una función simple con una sobrecarga para char y otra para whar_t. No utiliza nada no estándar, por lo que debería estar bien en cualquier plataforma.

La comparación de igualdad no considerará problemas como la codificación de longitud variable y la normalización Unicode, pero basic_string no tiene soporte para eso de lo que soy consciente de todos modos y normalmente no es un problema.

En los casos en que se requiera una manipulación lexicográfica más sofisticada del texto, simplemente debe usar una biblioteca de terceros como Boost, lo cual es de esperar.


2
¿Probablemente podría hacer esa función si la hiciera una plantilla y utilizara basic_string <T> en lugar de versiones separadas de string / wstring?
uliwitness

2
¿Cómo invocaría la plantilla de función única ya sea touperper o towupper sin recurrir al uso de especialización o macros, una sobrecarga de funciones parece una implementación más simple y más apropiada que cualquiera de las dos?
Neutrino

9

Corto y agradable. No hay otras dependencias, que la extensión estándar de C lib.

strcasecmp(str1.c_str(), str2.c_str()) == 0

devuelve verdadero si str1y str2son iguales. strcasecmppuede no existir, podría ser análogos stricmp, strcmpietc.

Código de ejemplo:

#include <iostream>
#include <string>
#include <string.h> //For strcasecmp(). Also could be found in <mem.h>

using namespace std;

/// Simple wrapper
inline bool str_ignoreCase_cmp(std::string const& s1, std::string const& s2) {
    if(s1.length() != s2.length())
        return false;  // optimization since std::string holds length in variable.
    return strcasecmp(s1.c_str(), s2.c_str()) == 0;
}

/// Function object - comparator
struct StringCaseInsensetiveCompare {
    bool operator()(std::string const& s1, std::string const& s2) {
        if(s1.length() != s2.length())
            return false;  // optimization since std::string holds length in variable.
        return strcasecmp(s1.c_str(), s2.c_str()) == 0;
    }
    bool operator()(const char *s1, const char * s2){ 
        return strcasecmp(s1,s2)==0;
    }
};


/// Convert bool to string
inline char const* bool2str(bool b){ return b?"true":"false"; }

int main()
{
    cout<< bool2str(strcasecmp("asd","AsD")==0) <<endl;
    cout<< bool2str(strcasecmp(string{"aasd"}.c_str(),string{"AasD"}.c_str())==0) <<endl;
    StringCaseInsensetiveCompare cmp;
    cout<< bool2str(cmp("A","a")) <<endl;
    cout<< bool2str(cmp(string{"Aaaa"},string{"aaaA"})) <<endl;
    cout<< bool2str(str_ignoreCase_cmp(string{"Aaaa"},string{"aaaA"})) <<endl;
    return 0;
}

Salida:

true
true
true
true
true

66
es extraño que C ++ std :: string tiene ningún método de comparación ignore-case ..
KYB

1
"strcasecmp no es parte del estándar" - Mark Ransom 1 de diciembre de 14 a las 19:57
Liviu

sí, pero la mayoría de los compiladores modernos lo tienen o su análogo con otro nombre. stricmp, strcmpi, strcasecmp, Etc Gracias. Mensaje editado.
kyb

TODO: use en cout << boolalphalugar de my bool2strporque implícitamente convierte bool a chars para stream.
kyb

Está en <strings.h> en las bibliotecas de gcc.
Búho

7

Hacer esto sin usar Boost se puede hacer obteniendo el puntero de cadena C con c_str()y usando strcasecmp:

std::string str1 ="aBcD";
std::string str2 = "AbCd";;
if (strcasecmp(str1.c_str(), str2.c_str()) == 0)
{
    //case insensitive equal 
}

6

Suponiendo que está buscando un método y no una función mágica que ya existe, francamente no hay mejor manera. Todos podríamos escribir fragmentos de código con trucos inteligentes para conjuntos de caracteres limitados, pero al final del día, en algún momento, debe convertir los caracteres.

El mejor enfoque para esta conversión es hacerlo antes de la comparación. Esto le permite una gran flexibilidad cuando se trata de esquemas de codificación, que su operador de comparación real debe ignorar.

Por supuesto, puede 'ocultar' esta conversión detrás de su propia función de cadena o clase, pero aún necesita convertir las cadenas antes de la comparación.


6

Escribí una versión que distingue entre mayúsculas y minúsculas de char_traits para usar con std :: basic_string para generar una cadena std :: que no distingue entre mayúsculas y minúsculas al hacer comparaciones, búsquedas, etc. utilizando las funciones integradas std :: basic_string.

En otras palabras, quería hacer algo como esto.

std::string a = "Hello, World!";
std::string b = "hello, world!";

assert( a == b );

... que std :: string no puede manejar. Aquí está el uso de mis nuevos char_traits:

std::istring a = "Hello, World!";
std::istring b = "hello, world!";

assert( a == b );

... y aquí está la implementación:

/*  ---

        Case-Insensitive char_traits for std::string's

        Use:

            To declare a std::string which preserves case but ignores case in comparisons & search,
            use the following syntax:

                std::basic_string<char, char_traits_nocase<char> > noCaseString;

            A typedef is declared below which simplifies this use for chars:

                typedef std::basic_string<char, char_traits_nocase<char> > istring;

    --- */

    template<class C>
    struct char_traits_nocase : public std::char_traits<C>
    {
        static bool eq( const C& c1, const C& c2 )
        { 
            return ::toupper(c1) == ::toupper(c2); 
        }

        static bool lt( const C& c1, const C& c2 )
        { 
            return ::toupper(c1) < ::toupper(c2);
        }

        static int compare( const C* s1, const C* s2, size_t N )
        {
            return _strnicmp(s1, s2, N);
        }

        static const char* find( const C* s, size_t N, const C& a )
        {
            for( size_t i=0 ; i<N ; ++i )
            {
                if( ::toupper(s[i]) == ::toupper(a) ) 
                    return s+i ;
            }
            return 0 ;
        }

        static bool eq_int_type( const int_type& c1, const int_type& c2 )
        { 
            return ::toupper(c1) == ::toupper(c2) ; 
        }       
    };

    template<>
    struct char_traits_nocase<wchar_t> : public std::char_traits<wchar_t>
    {
        static bool eq( const wchar_t& c1, const wchar_t& c2 )
        { 
            return ::towupper(c1) == ::towupper(c2); 
        }

        static bool lt( const wchar_t& c1, const wchar_t& c2 )
        { 
            return ::towupper(c1) < ::towupper(c2);
        }

        static int compare( const wchar_t* s1, const wchar_t* s2, size_t N )
        {
            return _wcsnicmp(s1, s2, N);
        }

        static const wchar_t* find( const wchar_t* s, size_t N, const wchar_t& a )
        {
            for( size_t i=0 ; i<N ; ++i )
            {
                if( ::towupper(s[i]) == ::towupper(a) ) 
                    return s+i ;
            }
            return 0 ;
        }

        static bool eq_int_type( const int_type& c1, const int_type& c2 )
        { 
            return ::towupper(c1) == ::towupper(c2) ; 
        }       
    };

    typedef std::basic_string<char, char_traits_nocase<char> > istring;
    typedef std::basic_string<wchar_t, char_traits_nocase<wchar_t> > iwstring;

2
Esto funciona para caracteres normales, pero no funcionará para todo Unicode, ya que la captura no es necesariamente bidireccional (hay un buen ejemplo en griego que implica sigma que no puedo recordar en este momento; algo así como tiene dos minúsculas y una mayúscula , y no puede obtener una comparación adecuada de ninguna manera)
coppro

1
Esa es realmente la forma incorrecta de hacerlo. La distinción entre mayúsculas y minúsculas no debería ser una propiedad de las cadenas en sí. ¿Qué sucede cuando el mismo objeto de cadena necesita comparaciones sensibles a mayúsculas y minúsculas?
Ferruccio

Si la distinción entre mayúsculas y minúsculas no es apropiada para ser "parte de" la cadena, tampoco lo es la función find (). Lo cual, para ti, podría ser cierto, y eso está bien. En mi opinión, lo mejor de C ++ es que no impone un paradigma particular al programador. Es lo que quieres / necesitas que sea.
John Dibling el

En realidad, creo que la mayoría de los gurús de C ++ (como los del comité de estándares) están de acuerdo en que fue un error colocar find () en std :: basic_string <> junto con muchas otras cosas que también podrían colocarse funciones libres Además, hay algunos problemas para ponerlo en el tipo.
Andreas Magnusson el

Como otros han señalado, hay dos cosas principales que están mal con esta solución (irónicamente, una es la interfaz y la otra es la implementación ;-)).
Konrad Rudolph el

4

He tenido una buena experiencia en el uso de los componentes internacionales para bibliotecas Unicode : son extremadamente potentes y proporcionan métodos para la conversión, soporte de configuración regional, representación de fecha y hora, asignación de casos (que no parece querer) y cotejo , que incluye la comparación entre mayúsculas y minúsculas (y más). Solo he usado la versión C ++ de las bibliotecas, pero parece que también tienen una versión Java.

Existen métodos para realizar comparaciones normalizadas a las que se refiere @Coincoin, e incluso pueden tener en cuenta la configuración regional, por ejemplo (y este es un ejemplo de clasificación, no estrictamente de igualdad), tradicionalmente en español (en España), la combinación de letras "ll" clasifica entre "l" y "m", entonces "lz" <"ll" <"ma".


4

Solo utilícelo strcmp()para la comparación entre mayúsculas y minúsculas y / strcmpi()o stricmp()para mayúsculas y minúsculas. Que están en el archivo de encabezado<string.h>

formato:

int strcmp(const char*,const char*);    //for case sensitive
int strcmpi(const char*,const char*);   //for case insensitive

Uso:

string a="apple",b="ApPlE",c="ball";
if(strcmpi(a.c_str(),b.c_str())==0)      //(if it is a match it will return 0)
    cout<<a<<" and "<<b<<" are the same"<<"\n";
if(strcmpi(a.c_str(),b.c_str()<0)
    cout<<a[0]<<" comes before ball "<<b[0]<<", so "<<a<<" comes before "<<b;

Salida

apple y ApPlE son lo mismo

a viene antes que b, entonces la manzana viene antes que la pelota


2
Voto negativo porque esta no es una forma de hacer las cosas en C ++.
Thomas Daugaard

Esta es la convención de C ++ en mi universidad, pero lo tendré en cuenta cuando publique aquí
reubenjohn

44
stricmp es una extensión de Microsoft AFAIK. BSD parece tener strcasecmp () en su lugar.
testigo del

3

Tarde a la fiesta, pero aquí hay una variante que usa std::locale, y por lo tanto maneja correctamente el turco:

auto tolower = std::bind1st(
    std::mem_fun(
        &std::ctype<char>::tolower),
    &std::use_facet<std::ctype<char> >(
        std::locale()));

le ofrece un functor que usa la configuración regional activa para convertir caracteres en minúsculas, que luego puede usar a través de std::transformpara generar cadenas en minúsculas:

std::string left = "fOo";
transform(left.begin(), left.end(), left.begin(), tolower);

Esto también funciona para wchar_tcadenas basadas.


2

Solo una nota sobre el método que finalmente elija, si ese método incluye el uso de strcmpeso, algunas respuestas sugieren:

strcmpno funciona con datos Unicode en general. En general, ni siquiera funciona con codificaciones Unicode basadas en bytes, como utf-8, ya que strcmpsolo hace comparaciones byte por byte y los puntos de código Unicode codificados en utf-8 pueden tomar más de 1 byte. El único caso Unicode específico que strcmpse maneja correctamente es cuando una cadena codificada con una codificación basada en bytes contiene solo puntos de código por debajo de U + 00FF, entonces la comparación byte por byte es suficiente.


2

A principios de 2013, el proyecto ICU, mantenido por IBM, es una respuesta bastante buena a esto.

http://site.icu-project.org/

ICU es una "biblioteca Unicode completa y portátil que sigue de cerca los estándares de la industria". Para el problema específico de la comparación de cadenas, el objeto Collation hace lo que desea.

El Proyecto Mozilla adoptó la UCI para la internacionalización en Firefox a mediados de 2012; puede seguir la discusión de ingeniería, incluidos los problemas de los sistemas de compilación y el tamaño del archivo de datos, aquí:


2

Parece que las soluciones anteriores no están utilizando el método de comparación e implementando el total nuevamente, así que aquí está mi solución y espero que funcione para usted (está funcionando bien).

#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
string tolow(string a)
{
    for(unsigned int i=0;i<a.length();i++)
    {
        a[i]=tolower(a[i]);
    }
    return a;
}
int main()
{
    string str1,str2;
    cin>>str1>>str2;
    int temp=tolow(str1).compare(tolow(str2));
    if(temp>0)
        cout<<1;
    else if(temp==0)
        cout<<0;
    else
        cout<<-1;
}

1

Si no desea usar la biblioteca Boost , aquí hay una solución para usar solo el encabezado io estándar de C ++.

#include <iostream>

struct iequal
{
    bool operator()(int c1, int c2) const
    {
        // case insensitive comparison of two characters.
        return std::toupper(c1) == std::toupper(c2);
    }
};

bool iequals(const std::string& str1, const std::string& str2)
{
    // use std::equal() to compare range of characters using the functor above.
    return std::equal(str1.begin(), str1.end(), str2.begin(), iequal());
}

int main(void)
{
    std::string str_1 = "HELLO";
    std::string str_2 = "hello";

    if(iequals(str_1,str_2))
    {
        std::cout<<"String are equal"<<std::endl;   
    }

    else
    {
        std::cout<<"String are not equal"<<std::endl;
    }


    return 0;
}

Creo que std :: toupper está en #include <cctype>, es posible que deba incluirlo.
David Ledger

Si va a usar una versión global como esta :: toupper, entonces es posible que no necesite incluir <ctype> porque hay dos versiones versión c y versión c ++ con configuración regional, supongo. Así que mejor usar la versión global ":: toupper ()"
HaSeeB MiR

esta solución falla cuando una de las cadenas está vacía: "" - devuelve verdadero en ese caso cuando debería devolver falso
ekkis

0

Si tiene que comparar una cadena fuente con más frecuencia con otras cadenas, una solución elegante es usar regex.

std::wstring first = L"Test";
std::wstring second = L"TEST";

std::wregex pattern(first, std::wregex::icase);
bool isEqual = std::regex_match(second, pattern);

Intenté esto pero error de compilación: error: conversion from 'const char [5]' to non-scalar type 'std::wstring {aka std::basic_string<wchar_t>}' requested
Deqing

mala idea. Es la peor solución.
Behrouz.M

Esta no es una buena solución, pero incluso si desea usarla, necesita una L delante de sus constantes de cadena ancha, por ejemplo, L "TEST"
celticminstrel

Sería bueno si alguien pudiera explicar por qué es la peor solución. ¿Por problemas de rendimiento? Crear la expresión regular es costoso, pero luego la comparación debería ser realmente rápida.
smibe

es utilizable y portátil, el principal problema es que primero no puede contener ningún carácter que use regex. No se puede usar como una comparación general de cadenas debido a eso. También será más lento, hay una bandera para que funcione como dice smibe, pero aún no se puede usar como una función general.
Ben

0

Una manera simple de comparar dos cadenas en c ++ (probado para Windows) es usando _stricmp

// Case insensitive (could use equivalent _stricmp)  
result = _stricmp( string1, string2 );  

Si está buscando usar con std :: string, un ejemplo:

std::string s1 = string("Hello");
if ( _stricmp(s1.c_str(), "HELLO") == 0)
   std::cout << "The string are equals.";

Para obtener más información aquí: https://msdn.microsoft.com/it-it/library/e0z9k731.aspx


Vale la pena leer stackoverflow.com/a/12414441/95309 además de esta respuesta, ya que es a) una función C yb) supuestamente no portátil.
Claus Jørgensen el

¿Qué #include necesitamos para que esto funcione?
ekkis

1
@ekkis para usar _stricmp, debe incluir <string.h> como puede leer aquí: docs.microsoft.com/en-us/cpp/c-runtime-library/reference/…
DAme 01 de

-1
bool insensitive_c_compare(char A, char B){
  static char mid_c = ('Z' + 'a') / 2 + 'Z';
  static char up2lo = 'A' - 'a'; /// the offset between upper and lowers

  if ('a' >= A and A >= 'z' or 'A' >= A and 'Z' >= A)
      if ('a' >= B and B >= 'z' or 'A' >= B and 'Z' >= B)
      /// check that the character is infact a letter
      /// (trying to turn a 3 into an E would not be pretty!)
      {
        if (A > mid_c and B > mid_c or A < mid_c and B < mid_c)
        {
          return A == B;
        }
        else
        {
          if (A > mid_c)
            A = A - 'a' + 'A'; 
          if (B > mid_c)/// convert all uppercase letters to a lowercase ones
            B = B - 'a' + 'A';
          /// this could be changed to B = B + up2lo;
          return A == B;
        }
      }
}

Esto probablemente podría hacerse mucho más eficiente, pero aquí hay una versión voluminosa con todos sus bits al descubierto.

no es tan portátil, pero funciona bien con lo que sea que esté en mi computadora (ni idea, soy de imágenes, no de palabras)


Este no es un soporte Unicode, que es lo que se hizo la pregunta.
Behrouz.M

Esto no admite juegos de caracteres que no estén en inglés.
Robert Andrzejuk

-3

Una manera fácil de comparar cadenas que solo son diferentes en minúsculas y mayúsculas es hacer una comparación ascii. Todas las letras mayúsculas y minúsculas difieren en 32 bits en la tabla ASCII, utilizando esta información tenemos lo siguiente ...

    for( int i = 0; i < string2.length(); i++)
    {
       if (string1[i] == string2[i] || int(string1[i]) == int(string2[j])+32 ||int(string1[i]) == int(string2[i])-32) 
    {
      count++;
      continue;
    }
    else 
    {
      break;
    }
    if(count == string2.length())
    {
      //then we have a match
    }
}

3
De acuerdo con esto, "++ j" se encontrará igual a "KKJ", y "1234" se encontrará igual a "QRST". Dudo que sea algo que alguien quiera.
celticminstrel
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.