Insensible a mayúsculas-de la comparación de cadenas en C++ [cerrado]

StackOverflow https://stackoverflow.com/questions/11635

  •  08-06-2019
  •  | 
  •  

Pregunta

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

Sírvanse indicar si los métodos son Unicode ambiente y cómo portátiles son.

¿Fue útil?

Solución

Boost incluye un práctico algoritmo 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
}

Otros consejos

Tome ventaja de la norma char_traits.Recordemos que un std::string es, de hecho, un typedef para std::basic_string<char>, o , más explícitamente, std::basic_string<char, std::char_traits<char> >.El char_traits tipo describe cómo los personajes comparar, cómo se copia, ¿cómo echaron etc.Todo lo que necesitas hacer es typedef una nueva cadena de más de basic_string, y lo dote con su propio personalizado char_traits que comparar caso 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 se encuentran en la Gurú de La Semana número 29.

El problema con boost es que usted tiene que vincular con y dependen de impulso.No es fácil en algunos casos (por ejemplo,android).

Y el uso de char_traits significa todos las comparaciones son sensibles a mayúsculas y minúsculas, que no suelen ser lo que usted desea.

Esto debería ser suficiente.Debe ser razonablemente eficiente.No soporta unicode o nada, aunque.

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:Bono de 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);
                      });
}

Si usted está en un sistema POSIX, puede utilizar strcasecmp.Esta función no es parte del estándar de C, sin embargo, ni está disponible en Windows.Se realizará una mayúsculas y minúsculas en comparación de 8 bits, caracteres, siempre y cuando la configuración regional es POSIX.Si la localización no es POSIX, los resultados son indefinidos (por lo que es posible hacer un localizada comparar, o puede que no).Un gran personaje equivalente no está disponible.

En su defecto, un gran número de histórico de la biblioteca de C de implementaciones de las funciones stricmp() y strnicmp().Visual C++ en Windows cambiado el nombre de todos estos por un prefijo con un carácter de subrayado, porque no son parte del estándar ANSI, por lo que en ese sistema se llaman _stricmp o _strnicmp.Algunas bibliotecas también pueden tener caracteres anchos o multibyte funciones equivalentes (normalmente llamado por ej.wcsicmp, mbcsicmp y así sucesivamente).

C y C++ son en gran parte ignorante de los temas de internacionalización, por lo que no hay una buena solución a este problema, excepto para el uso de una librería de terceros.Echa un vistazo IBM ICU (Internacional de Componentes para Unicode) si usted necesita una sólida biblioteca de C/C++.La UCI es tanto para sistemas Windows y Unix.

Estás hablando de un tonto insensible a mayúsculas-minúsculas comparar o un completo normalizado Unicode comparar?

Un tonto comparar no encontrar cadenas que podría ser el mismo, pero no son binarias igual.

Ejemplo:

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

Son todos equivalentes, pero también tienen diferentes representaciones binarias.

Que dijo, Normalización De Unicode debería ser obligatorio leer especialmente si usted planea en el apoyo a Hangul, Tailandesa y otros idiomas asiáticos.

Además, IBM bastante patentado más optimizada Unicode algoritmos y les hizo a disposición del público.También mantienen una aplicación : IBM ICU

boost::iequals no es utf-8 compatible en el caso de la cadena.Puede utilizar boost::locale.

comparator<char,collator_base::secondary> cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;
  • Primaria -- ignorar los acentos y caracteres caso, la comparación de la base de sólo letras.Por ejemplo, la "fachada" y "Fachada" son los mismos.
  • Secundaria: ignorar mayúsculas y minúsculas, pero considerar los acentos."fachada" y "fachada" son diferentes, pero "Fachada" y "fachada" son los mismos.
  • Terciario-considere los dos casos y los acentos:"Fachada" y "fachada" son diferentes.Ignorar los signos de puntuación.
  • Cuaternario-considere en todo caso, los acentos y los signos de puntuación.Las palabras deben ser idénticos en términos de representación Unicode.
  • Idénticos -- como cuaternario, pero al comparar los puntos de código así.

Mi primer pensamiento para un no-unicode versión era 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;
}

Puede utilizar strcasecmp en Unix, o stricmp en Windows.

Una cosa que no ha sido mencionado hasta ahora es que si usted está utilizando stl cadenas con estos métodos, es útil para la primera comparar la longitud de las dos cadenas, ya que esta información ya está disponible para usted en la clase string.Esto podría evitar haciendo el costoso de comparación de cadena si las dos cadenas se comparan ni siquiera son de la misma longitud en el primer lugar.

Visual C++ las funciones de cadena de apoyo de unicode: http://msdn.microsoft.com/en-us/library/cc194799.aspx

el que usted está probablemente en busca de es _wcsnicmp

Estoy tratando de improvisar una buena respuesta de todos los mensajes, así que me ayude a editar esto:

Aquí es un método de hacer esto, a pesar de la transformación de las cadenas, y no es Unicode ambiente, que debe ser portátil que es un plus:

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 este es más portátil que stricmp() porque stricmp() no es en realidad parte de la ets de la biblioteca, pero sólo implementada por la mayoría de los proveedores de compiladores.

Para conseguir una verdadera Unicode amable aplicación que aparece, usted debe ir fuera de la ets de la biblioteca.Una buena parte 3ª de la biblioteca es el IBM ICU (Internacional de Componentes para Unicode)

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

El Boost.Cadena la biblioteca tiene una gran cantidad de algoritmos para hacer caso-insenstive comparaciones y así sucesivamente.

Usted podría implementar su propio, pero ¿por qué molestarse cuando ya se ha hecho?

FYI, strcmp() y stricmp() son vulnerables a un desbordamiento de búfer, ya que sólo el proceso hasta que lleguen a un terminador nulo.Es más seguro utilizar _strncmp() y _strnicmp().

Para mi básicas de mayúsculas y minúsculas de la cadena de comparación necesidades prefiero no tener que usar una biblioteca externa, ni tampoco quiero separado una clase de cadena con mayúsculas y minúsculas rasgos que es incompatible con todas mis otras cadenas.

Así que lo que he encontrado es este:

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 sencilla, con una sobrecarga para char y otro para whar_t.No uso nada que no estándar, así que debe estar bien en cualquier plataforma.

La comparación de igualdad no considerar cuestiones tales como la longitud de la variable codificación y normalización de Unicode, pero basic_string no tiene soporte para que, que yo sepa, de todos modos y no es normalmente un problema.

En los casos donde más sofisticados lexicográfico de la manipulación de texto es necesario, entonces usted simplemente tiene que usar una biblioteca de terceros como Boost, que es de esperar.

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

Usted puede utilizar el código de arriba en C++14 si usted no está en una posición para usar el boost.Usted tiene que usar std::towlower para los distintos caracteres.

Corto y agradable.No otras dependencias, que extendida std C lib.

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

devuelve cierto si str1 y str2 son iguales.strcasecmp no puede existir, no podría ser análogos stricmp, strcmpi, etc.

Ejemplo de código:

#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

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

Demo

Suponiendo que usted está buscando un método y no una función mágica que ya existe, no es, francamente, no hay mejor manera.Todos podemos escribir fragmentos de código con trucos inteligentes para la limitación de conjuntos de caracteres, pero al final del día en el somepoint usted tiene que convertir los caracteres.

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

Usted puede, por supuesto, 'ocultar' esta conversión detrás de su propia cadena de función o clase, pero usted todavía necesita para convertir las cadenas antes de la comparación.

Escribí un insensible a mayúsculas-versión de char_traits para su uso con std::basic_string con el fin de generar un std::string, que no es sensible a mayúsculas al hacer comparaciones, búsquedas, etc el uso de la incorporada en el std::basic_string funciones miembro.

Entonces, 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 mi nuevo char_traits:

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

assert( a == b );

...y aquí está la aplicació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;

Haciendo esto, sin usar el Boost se puede hacer mediante la obtención de la C puntero de cadena con c_str() y el uso de strcasecmp:

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

He tenido buena experiencia en el uso de la Internacional de Componentes para Unicode bibliotecas - son extremadamente poderosos, y proporcionar métodos para la conversión, soporte local, fecha y hora de la representación, caso de asignación (que no parecen querer), y intercalación, que incluye mayúsculas y los acentos de comparación (y más).Solo he usado la versión de C++ de las bibliotecas, pero que parecen tener una versión de Java así.

Existen métodos para realizar normalizado compara según lo mencionado por @Coincoin, y puede incluso cuenta para la configuración regional, por ejemplo, (y este es un ejemplo de clasificación, no estrictamente de la igualdad), tradicionalmente en español (en España), la combinación de letras "ll" se ordena entre "l" y "m", por lo que "lz" < "ll". < "ma".

Sólo uso strcmp() para el caso sensibles y strcmpi() o stricmp() para el caso insensible comparación.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 los mismos

una viene antes que b, por lo que apple viene antes que la pelota

Solo una nota en cualquiera que sea el método que, finalmente, elegir, si ese método pasa a incluir el uso de strcmp que algunas respuestas sugieren:

strcmp no funciona con los datos de Unicode en general.En general, incluso no trabajar con bytes codificaciones Unicode, como utf-8, ya que strcmp sólo hace byte por byte comparaciones y puntos de código Unicode codificados en utf-8 puede tomar más de 1 byte.La única específicos Unicode caso strcmp manejar adecuadamente es cuando una cadena codificada con un byte de codificación basado sólo contiene el código de puntos por debajo de U+00FF -, el byte por byte de comparación es suficiente.

A partir de principios de 2013, la unidad de cuidados intensivos del proyecto, mantenido por IBM, es una muy buena respuesta a este.

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

La UCI es una "completa, portátil Unicode biblioteca cerca de las pistas de los estándares de la industria." Para el problema específico de la cadena de comparación, la Intercalación de objeto hace lo que quiere.

El Proyecto Mozilla adoptado la UCI para la internacionalización en Firefox a mediados de 2012;usted puede realizar el seguimiento de la ingeniería de discusión, incluyendo las cuestiones de construir sistemas y tamaño de archivo de datos, aquí:

Tarde a la fiesta, pero aquí es una variante que utiliza std::locale, y así se controla correctamente turco:

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

le da un functor que utiliza el activo de la configuración regional para convertir los caracteres a minúsculas, que luego se puede utilizar a través de los std::transform para generar el menor caso de las cadenas:

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

Esto también funciona para wchar_t basado en las cadenas.

Parece soluciones anteriores no utiliza el método de comparación y la implementación total del nuevo así que aquí está mi solución y espero que funcione para usted (funciona 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;
}

Si usted no desea utilizar Impulso de la biblioteca entonces aquí está la solución a ello utilizando sólo el estándar de C++ io encabezado.

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

Si usted tiene un vector de cadenas de caracteres, por ejemplo:

std::sort(std::begin(myvector), std::end(myvector), [](std::string const &a, std::string const &b)
{
    return std::lexicographical_compare(std::begin(a), std::end(a), std::begin(b), std::end(b), [](std::string::value_type a, std::string::value_type b)
    {
        return std::tolower(a) < std::tolower(b); //case-insensitive
    });
});

http://ideone.com/N6sq6X

Si usted tiene que comparar una cadena de origen más a menudo con otras cadenas, una solución elegante es usar expresiones regulares.

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

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

Una manera sencilla de comparar dos string en c++ (probado en windows) es el uso de _stricmp

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

Si usted está buscando para utilizar con std::string, un ejemplo:

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

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

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 ser mucho más eficiente, pero aquí es un abultamiento de la versión con todos sus bits desnudo.

no todos los que portátil, pero funciona bien con cualquier cosa que esté en mi ordenador (ni idea, yo soy de imágenes no las palabras)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top