Comparación de cadenas que no distingue entre mayúsculas y minúsculas en C++ [closed]

9 minutos de lectura

Comparación de cadenas que no distingue entre mayúsculas y minúsculas en C++ [closed]
Adán

¿Cuál es la mejor manera de hacer una comparación de cadenas que no distingue entre mayúsculas y minúsculas en C++ sin transformar una cadena a 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 usar std::stricmp. De lo contrario, lea lo que Herb tiene que decir.

    – Konrad Rodolfo

    26 de agosto de 2008 a las 12:17

  • En c, por lo general, uno se vio obligado a subir toda la cadena y luego comparar de esa manera, o lanzar su propia comparación: P

    –Michael Dorgan

    22 de mayo de 2010 a las 1:10

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

    – Moż

    5 de noviembre de 2013 a las 21:39

  • @Mσᶎ esta pregunta también tiene esa respuesta, con la importante salvedad de que strcasecmp no es parte del estándar y falta en al menos un compilador común.

    – Mark Ransom

    1 de diciembre de 2014 a las 19:57

Comparación de cadenas que no distingue entre mayúsculas y minúsculas en C++ [closed]
Robar

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
}

  • ¿Es compatible con UTF-8? Yo creo que no.

    – vladr

    30 de octubre de 2010 a las 0:23

  • No, porque UTF-8 permite codificar cadenas idénticas con diferentes códigos binarios, debido a acentos, combinaciones, problemas de bidi, etc.

    – vy32

    18 de junio de 2011 a las 23:35

  • @ 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.

    – Mago

    10 de noviembre de 2011 a las 23:44


  • @Wiz, está ignorando el problema de la normalización de cadenas Unicode. ñ se puede representar como una ˜ combinada seguida de una n, o con un carácter ñ. Debe usar la normalización de cadenas Unicode antes de realizar la comparación. Revise el Informe técnico de Unicode n.º 15, unicode.org/reports/tr15

    – vy32

    11 de noviembre de 2011 a las 3:21

  • @wonkorealtime: porque “ß” convertido a mayúsculas es “SS”: formato de archivo.info/info/unicode/char/df/index.htm

    – Pato mugido

    29 mayo 2014 a las 23:11


Comparación de cadenas que no distingue entre mayúsculas y minúsculas en C++ [closed]
guillermo

Aprovecha el estándar char_traits. Recuerda 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> >. los char_traits type describe cómo se comparan los caracteres, cómo se copian, cómo se emiten, etc. Todo lo que necesita hacer es escribir una nueva cadena sobre basic_stringy proporcionarlo con su propia costumbre char_traits que comparan mayúsculas y minúsculas insensiblemente.

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 Gurú de la semana número 29.

  • Por lo que sé por mi propia experimentación, esto hace que su nuevo tipo de cadena sea incompatible con std::string.

    – Zan Lince

    26/09/2012 a las 21:25

  • 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

    9 oct 2012 a las 9:24


  • “Todo lo que necesitas hacer…”

    – Tim MB

    19 de abril de 2013 a las 10:03

  • Cualquier construcción del lenguaje que fuerce tal locura en este caso trivial debe y puede ser abandonada sin remordimientos.

    – Erik Aronesty

    14 de noviembre de 2014 a las 14:17


  • @DaveKennedy Creo que Erik aconseja abandonar los lenguajes humanos, ya que esos son las construcciones del lenguaje las que están forzando esta locura. 🙂

    – srm

    21 de marzo de 2018 a las 16:35

Comparación de cadenas que no distingue entre mayúsculas y minúsculas en C++ [closed]
timmmm

El problema con boost es que tienes que vincularte y depender de boost. No es fácil en algunos casos (por ejemplo, android).

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

Esto debería ser suficiente. Debe 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: versión adicional C++14 (#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);
                      });
}

Actualización: versión C ++ 20 usando std::ranges:

#include <ranges>
#include <algorithm>
#include <string>

bool iequals(const std::string_view& lhs, const std::string_view& rhs) {
    auto to_lower{ std::ranges::views::transform(std::tolower) };
    return std::ranges::equal(lhs | to_lower, rhs | to_lower);
}

  • En realidad, la biblioteca de cadenas de refuerzo 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 fuentes, por lo que no necesita requerir la biblioteca boost completa.

    – Gretchen

    9 de marzo de 2011 a las 21:47


  • Es bueno conocer una versión simple y sin dependencia de impulso.

    – Deqing

    17 de mayo de 2014 a las 3:31

  • La biblioteca de texto @Anna de impulso debe construirse y vincularse. Utiliza la UCI de IBM.

    – Behrouz.M

    1 de junio de 2015 a las 6:46


  • std::tolower deberían no ser llamado char directamente, un static_cast para unsigned char se necesita

    – Evo

    26 de septiembre de 2020 a las 9:50


  • @Timmmm Me he tomado la libertad de agregar una versión de C++ 20 a esta respuesta, ya que creo que esta es la mejor opción y, en comparación con otras respuestas en este hilo, creo que se parece más a sus otras soluciones.

    – Ben Cottrell

    2 ene a las 11:39


Comparación de cadenas que no distingue entre mayúsculas y minúsculas en C++ [closed]
parque derek

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 sin distinción entre mayúsculas y minúsculas en caracteres de 8 bits, siempre que la configuración regional 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). No hay disponible un equivalente de caracteres anchos.

De lo contrario, una gran cantidad de implementaciones históricas de la biblioteca C tienen las funciones stricmp() y strnicmp(). Visual C ++ en Windows cambió el nombre de todos estos prefijándolos con un guión bajo 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 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 usar una biblioteca de terceros. Verificar IBM ICU (Componentes internacionales para Unicode) si necesita una biblioteca robusta para C/C++. ICU es para sistemas Windows y Unix.

Comparación de cadenas que no distingue entre mayúsculas y minúsculas en C++ [closed]
moneda

¿Está hablando de una comparación tonta que no distingue entre 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).

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

Dicho esto, Normalización Unicode debería ser una lectura obligatoria, especialmente si planea admitir Hangul, Thai y otros idiomas asiáticos.

Además, IBM prácticamente patentó la mayoría de los algoritmos Unicode optimizados y los puso a disposición del público. También mantienen una implementación: Unidad de cuidados intensivos de IBM

Comparación de cadenas que no distingue entre mayúsculas y minúsculas en C++ [closed]
Igor Milyakov

boost::iequals no es compatible con utf-8 en el caso de cadenas. Puedes usar impulso::locale.

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

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(static_cast<unsigned char>(*c1)) != tolower(static_cast<unsigned char>(*c2))) {
            return false;
        }
    }
    return true;
}

  • std::tolower deberían no ser llamado char directamente, un static_cast para unsigned char se necesita

    – Evo

    26 de septiembre de 2020 a las 9:50


  • @Evg, entonces if (tolower(static_cast<unsigned char>(*c1)) != tolower(static_cast<unsigned char>(*c2)) ¿servirá?

    – Sombra2531

    27 sep 2020 a las 10:51

  • Sí, esta debería ser la forma correcta.

    – Evo

    27 de septiembre de 2020 a las 11:54

¿Ha sido útil esta solución?