Comparaison des chaînes de caractères non sensibles à la casse en C++ [fermé]
Quelle est la meilleure façon de faire une comparaison de chaîne non sensible à la casse en C++ sans transformer une chaîne en majuscules ou en minuscules?
veuillez indiquer si les méthodes sont compatibles avec les Unicodes et dans quelle mesure elles sont portables.
30 réponses
coup de pouce comprend une pratique de l'algorithme:
#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
}
profitez de la norme char_traits
. Rappelons qu'un std::string
est en fait un typedef pour std::basic_string<char>
, ou plus explicitement, std::basic_string<char, std::char_traits<char> >
. Le type char_traits
décrit comment les caractères se comparent, comment ils copient, comment ils moulent, etc. Tout ce que vous devez faire est de taper une nouvelle chaîne de caractères sur basic_string
, et de le fournir avec votre propre coutume char_traits
qui comparent cas insensivement.
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;
les détails sont sur Guru du nombre de la semaine 29 .
parlez-vous d'un comparateur stupide et insensible ou D'un comparateur Unicode complètement normalisé?
une comparaison stupide ne trouvera pas de chaînes qui pourraient être les mêmes mais qui ne sont pas binaires égales.
exemple:
U212B (ANGSTROM SIGN)
U0041 (LATIN CAPITAL LETTER A) + U030A (COMBINING RING ABOVE)
U00C5 (LATIN CAPITAL LETTER A WITH RING ABOVE).
sont tous équivalents mais ils ont également différentes représentations binaires.
cela dit, Normalisation Unicode devrait être obligatoire de le lire, surtout si vous prévoyez de supporter Hangul, le Thaï et d'autres langues asiatiques.
IBM a également breveté la plupart des algorithmes Unicode optimisés et les a rendus accessibles au public. Ils maintiennent également une mise en œuvre: IBM ICU
si vous êtes sur un système POSIX, vous pouvez utiliser strcasecmp . Cette fonction ne fait pas partie de la norme C, cependant, ni est-il disponible sur Windows. Ceci va effectuer une comparaison non sensible à la casse sur les caractères 8 bits, à condition que la locale soit POSIX. Si la locale n'est pas POSIX, les résultats ne sont pas définis (donc il pourrait faire une comparaison localisée, ou il pourrait ne pas). Un grand caractère équivalent n'est pas disponible.
faute de quoi, un grand nombre de C les implémentations de bibliothèque ont les fonctions stricmp () et strnicmp (). Visual C++ sur Windows renommé tous ceux-ci en les préfixant avec un underscore parce qu'ils ne font pas partie de la norme ANSI, donc sur ce système ils sont appelés _stricmp ou _strnicmp . Certaines bibliothèques peuvent aussi avoir des fonctions équivalentes à des caractères larges ou à plusieurs octets (généralement nommées par exemple wcsicmp, mbcsicmp et ainsi de suite).
C et c++ sont tous deux largement ignorants de l'internationalisation problèmes, donc il n'y a pas de bonne solution à ce problème, sauf pour utiliser une bibliothèque tierce partie. Consultez IBM ICU (International Components for Unicode) si vous avez besoin d'une bibliothèque robuste pour C/C++. ICU est pour les systèmes Windows et Unix.
le problème avec boost est que vous avez à lier avec et dépendent de boost. Pas facile dans certains cas (par exemple android).
et l'utilisation de char_traits signifie tous vos comparaisons sont insensibles à la casse, ce qui n'est généralement pas ce que vous voulez.
cela devrait suffire. Il devrait être raisonnablement efficace. Ne gère pas l'unicode ou quoi que ce soit.
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;
}
mise à jour: Bonus C++14 version ( #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);
});
}
ma première pensée pour une version non unicode était de faire quelque chose comme ceci:
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;
}
boost:: iequals n'est pas compatible utf-8 dans le cas de string. Vous pouvez utiliser boost::locale .
comparator<char,collator_base::secondary> cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;
- primaire -- ignorer les accents et le cas des caractères, en comparant les lettres de base seulement. Par exemple" façade "et" Façade " sont les mêmes.
- secondaire -- ignorer le cas des caractères, mais considérer les accents. "façade" et "façade" sont différentes mais "Façade" et "façade" sont identiques.
- Tertiaire -- considérons à la fois le cas et les accents: "Façade" et "façade" sont différents. Ignorez la ponctuation.
- quaternaire -- considérez tous les cas, les accents et la ponctuation. Les mots doivent être identiques en termes de représentation Unicode.
- identique -- comme quaternaire, mais comparez les points de code aussi.
vous pouvez utiliser strcasecmp
sur Unix, ou stricmp
sur Windows.
une chose qui n'a pas été mentionnée jusqu'à présent est que si vous utilisez des chaînes stl avec ces méthodes, il est utile de comparer d'abord la longueur des deux chaînes, puisque cette information est déjà disponible pour vous dans la classe string. Cela pourrait empêcher de faire la comparaison de chaîne coûteuse si les deux chaînes que vous comparez ne sont même pas de la même longueur en premier lieu.
Visual C++ fonctions de chaîne de la prise en charge unicode: http://msdn.microsoft.com/en-us/library/cc194799.aspx
celui que vous cherchez probablement est _wcsnicmp
j'essaie de trouver une bonne réponse dans tous les billets, alors aidez-moi à éditer ceci:
Voici une méthode pour faire ceci, bien qu'il ne transforme les cordes, et N'est pas Unicode amical, il devrait être portable qui est 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 );
}
D'après ce que j'ai lu, c'est plus portable que stricmp() parce que stricmp() n'est pas en fait une partie de la bibliothèque std, mais seulement implémentée par la plupart des fournisseurs de compilateurs.
pour obtenir une implémentation vraiment conviviale Unicode, il semble que vous devez sortir de la bibliothèque std. Une bonne bibliothèque de tiers est le IBM ICU (International Components for Unicode)
aussi boost:: iequals fournit une assez bonne utilité pour faire ce genre de comparaison.
Le Coup De Pouce.String bibliothèque a beaucoup d'algorithmes pour faire des comparaisons cas-insensitive et ainsi de suite.
vous pourriez mettre en œuvre votre propre, mais pourquoi s'inquiéter quand il a déjà été fait?
pour info, strcmp()
et stricmp()
sont vulnérables à débordement de la mémoire tampon, puisqu'ils ont juste processus jusqu'à ce que ils ont frappé un terminateur null. Il est plus sûr d'utiliser _strncmp()
et _strnicmp()
.
pour mes besoins de base de comparaison de chaînes non sensibles à la casse, je préfère ne pas avoir à utiliser une bibliothèque externe, pas plus que je ne veux une classe de chaîne séparée avec des traits non sensibles à la casse qui est incompatible avec toutes mes autres chaînes.
alors ce que j'ai trouvé c'est:
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); });
}
une fonction simple avec une surcharge pour char et une autre pour whar_t. N'utilise rien de non standard donc devrait être très bien sur n'importe quelle plate-forme.
la comparaison d'égalité ne tiendra pas compte de questions comme l'encodage de longueur variable et la normalisation Unicode, mais basic_string n'a pas de support pour cela que je suis au courant de toute façon et ce n'est pas normalement un problème.
dans les cas où une manipulation lexicographique plus sophistiquée du texte est nécessaire, alors vous devez simplement utiliser une bibliothèque tierce comme Boost, ce qui est à prévoir.
std::equal(str1.begin(), str1.end(), str2.begin(), [](auto a, auto b){return std::tolower(a)==std::tolower(b);})
vous pouvez utiliser le code ci-dessus en C++14 Si vous n'êtes pas en mesure d'utiliser boost. Vous devez utiliser std::towlower
pour les larges chars.
court et sympa. Pas d'autres dépendances, que extended std C lib.
strcasecmp(str1.c_str(), str2.c_str()) == 0
retourne true si str1
et str2
sont égaux.
strcasecmp
peut ne pas exister, il pourrait y avoir des analogues stricmp
, strcmpi
, etc.
exemple de code:
#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;
}
sortie:
true
true
true
true
true
voir 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;
}
en supposant que vous êtes à la recherche d'une méthode et non d'une fonction magique qui existe déjà, il n'y a franchement pas de meilleure façon. Nous pourrions tous écrire des extraits de code avec des astuces astucieuses pour des jeux de caractères limités, mais à la fin de la journée à un point vous devez convertir les caractères.
La meilleure approche pour cette conversion est à faire avant la comparaison. Cela vous permet une bonne dose de flexibilité en ce qui concerne les schémas d'encodage, que votre comparaison réelle l'opérateur doit être ignorants de.
vous pouvez bien sûr "cacher" cette conversion derrière votre propre fonction de chaîne ou classe, mais vous avez encore besoin de convertir les chaînes avant la comparaison.
j'ai écrit une version de char_traits non sensible à la casse pour l'utiliser avec std::basic_string afin de générer une chaîne de caractères std::qui n'est pas sensible à la casse en faisant des comparaisons, des recherches, etc en utilisant les fonctions de membre STD::basic_string.
donc en d'autres termes, je voulais faire quelque chose comme ça.
std::string a = "Hello, World!";
std::string b = "hello, world!";
assert( a == b );
...qui std::string ne peut pas gérer. Voici l'usage de mes nouveaux char_traits:
std::istring a = "Hello, World!";
std::istring b = "hello, world!";
assert( a == b );
...et voici la mise en œuvre:
/* ---
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;
faire ceci sans utiliser Boost peut être fait en obtenant le pointeur de chaîne C avec c_str()
et en utilisant strcasecmp
:
std::string str1 ="aBcD";
std::string str2 = "AbCd";;
if (strcasecmp(str1.c_str(), str2.c_str()) == 0)
{
//case insensitive equal
}
j'ai eu une bonne expérience en utilisant le composants internationaux pour les bibliothèques Unicode - ils sont extrêmement puissants, et fournissent des méthodes pour la conversion, le soutien local, la date et l'Heure de rendu, la cartographie de cas (que vous ne semblez pas vouloir), et collation , qui comprend la comparaison cas - et l'accent-insensible (et plus). J'ai seulement utilisé la version C++ des bibliothèques, mais il semble qu'elles aient aussi une version Java.
des méthodes existent pour effectuer des comparaisons normalisées comme le mentionne @Coincoin, et peuvent même tenir compte de locale - par exemple (et ceci un exemple de tri, pas strictement l'égalité), traditionnellement en espagnol (en Espagne), la combinaison de lettres "ll" trie entre "l" et "m", donc "lz" < "ll" < "ma".
il suffit d'utiliser strcmp()
pour la comparaison sensible à la casse et strcmpi()
ou stricmp()
pour la comparaison non sensible à la casse. Qui sont tous les deux dans le fichier d'en-tête <string.h>
format:
int strcmp(const char*,const char*); //for case sensitive
int strcmpi(const char*,const char*); //for case insensitive
Utilisation:
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;
Sortie
pomme et pomme sont les mêmes
a vient avant b, donc pomme vient avant balle
juste une note sur la méthode que vous avez finalement choisi, si cette méthode inclut l'utilisation de strcmp
que certaines réponses suggèrent:
strcmp
ne fonctionne pas avec les données Unicode en général. En général, il ne fonctionne même pas avec les encodages Unicode basés sur byte, comme utf-8, puisque strcmp
ne fait que des comparaisons byte-par-byte et les points de code Unicode encodés dans utf-8 peuvent prendre plus de 1 byte. Le seul cas spécifique Unicode strcmp
correctement handle est lorsqu'une chaîne de caractères encodée avec un encodage basé sur un octet ne contient que des points de code inférieurs à U+00FF-alors la comparaison byte - par-octet est suffisante.
depuis le début de 2013, le projet ICU, maintenu par IBM, est une assez bonne réponse à cela.
ICU est une bibliothèque complète, portable Unicode qui suit de près les normes de l'industrie."Pour le problème spécifique de la comparaison des chaînes, L'objet Collation fait ce que vous voulez.
le projet Mozilla a adopté ICU pour l'internationalisation dans Firefox à la mi-2012; vous peut suivre la discussion d'ingénierie, y compris les questions de systèmes de construction et la taille des fichiers de données, ici:
en retard à la partie, Mais voici une variante qui utilise std::locale
, et donc manipule correctement le turc:
auto tolower = std::bind1st(
std::mem_fun(
&std::ctype<char>::tolower),
&std::use_facet<std::ctype<char> >(
std::locale()));
vous donne une fonction qui utilise la locale active pour convertir les caractères en minuscules, que vous pouvez ensuite utiliser via std::transform
pour générer des chaînes de minuscules:
std::string left = "fOo";
transform(left.begin(), left.end(), left.begin(), tolower);
cela fonctionne aussi pour les chaînes à base de wchar_t
.
ressemble à des solutions ci-dessus ne sont pas en utilisant la méthode de comparaison et de mise en œuvre total encore une fois donc voici ma solution et l'espoir qu'il fonctionne pour vous (il fonctionne très 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 vous avez un vecteur de chaînes, par exemple:
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
});
});
si vous devez comparer plus souvent une chaîne source avec d'autres chaînes, une solution élégante est d'utiliser 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);
une façon simple de comparer deux chaînes en c++ (testé pour windows) est d'utiliser _stricmp
// Case insensitive (could use equivalent _stricmp)
result = _stricmp( string1, string2 );
Si vous cherchez à utiliser avec std::string, un exemple:
std::string s1 = string("Hello");
if ( _stricmp(s1.c_str(), "HELLO") == 0)
std::cout << "The string are equals.";
pour plus d'informations ici: https://msdn.microsoft.com/it-it/library/e0z9k731.aspx
si vous ne voulez pas utiliser Boost library alors voici la solution en utilisant seulement l'en-tête c++ io standard.
#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;
}
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;
}
}
}
cela pourrait probablement être rendu beaucoup plus efficace, mais voici une version volumineuse avec tous ses morceaux nus.
pas tout ce que portable, mais fonctionne bien avec ce qui est sur mon ordinateur (aucune idée, je suis d'Images pas de mots)