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.
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
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_string
y 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> > istring
notypedef 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
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 llamadochar
directamente, unstatic_cast
paraunsigned 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
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.
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
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 llamadochar
directamente, unstatic_cast
paraunsigned 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
@[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