Comparaison de chaînes insensible à la casse en C ++ [fermé]

373

Quelle est la meilleure façon de faire une comparaison de chaînes insensible à la casse en C ++ sans transformer une chaîne en majuscules ou en minuscules?

Veuillez indiquer si les méthodes sont compatibles avec Unicode et à quel point elles sont portables.

Adam
la source
@ [Adam] (# 11679): Bien que cette variante soit bonne en termes de convivialité, elle est mauvaise en termes de performances car elle crée des copies inutiles. Je pourrais oublier quelque chose mais je crois que la meilleure façon (non-Unicode) est d'utiliser std::stricmp. Sinon, lisez ce que Herb a à dire .
Konrad Rudolph
En c, on a généralement été forcé de toucher toute la chaîne, puis de comparer de cette façon - ou de lancer votre propre comparaison: P
Michael Dorgan
une question ultérieure a une réponse plus simple: strcasecmp (au moins pour les compilateurs BSD et POSIX) stackoverflow.com/questions/9182912/…
Móż
@ Mσᶎ cette question a également cette réponse, avec l'importante mise en garde qui strcasecmpne fait pas partie de la norme et qui manque dans au moins un compilateur commun.
Mark Ransom

Réponses:

318

Boost comprend un algorithme pratique pour cela:

#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
}
Rob
la source
14
Cet UTF-8 est-il convivial? Je crois que non.
Vladr
18
Non, car UTF-8 permet de coder des chaînes identiques avec des codes binaires différents, en raison d'accents, de combinaisons, de problèmes bidi, etc.
vy32
10
@ vy32 C'est absolument incorrect! Les combinaisons UTF-8 s'excluent mutuellement. Il doit toujours utiliser une représentation la plus courte possible, sinon, c'est une séquence UTF-8 ou un point de code mal formé qui doit être traité avec soin.
Wiz
48
@Wiz, vous ignorez le problème de la normalisation des chaînes Unicode. ñ peut être représenté par une combinaison ˜ suivie d'un n ou d'un caractère ñ. Vous devez utiliser la normalisation de chaîne Unicode avant d'effectuer la comparaison. Veuillez consulter le rapport technique Unicode n ° 15, unicode.org/reports/tr15
vy32
12
@wonkorealtime: parce que "ß" converti en majuscules est "SS": fileformat.info/info/unicode/char/df/index.htm
Mooing Duck
118

Profitez de la norme char_traits. Rappelons qu'un std::stringest en fait un typedef pour std::basic_string<char>, ou plus explicitement, std::basic_string<char, std::char_traits<char> >. Le char_traitsgenre décrit comment les personnages se comparent, comment ils copient, comment ils jettent etc. Tout ce que vous devez faire est typedef une nouvelle chaîne sur basic_string, et de fournir avec votre propre coutume char_traitsqui comparent insensible à la casse.

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 le gourou de la semaine numéro 29 .

wilhelmtell
la source
10
Pour autant que je sache d'après ma propre expérimentation, cela rend votre nouveau type de chaîne incompatible avec std :: string.
Zan Lynx
8
Bien sûr, c'est le cas - pour son propre bien. Une chaîne insensible à la casse est autre chose: typedef std::basic_string<char, ci_char_traits<char> > istringnon typedef std::basic_string<char, std::char_traits<char> > string.
Andreas Spindler
232
"Tout ce que vous devez faire ..."
Tim MB
3
@Nathan utilise probablement un compilateur capable d'exécuter un CSE de base sur le code ...
The Paramagnetic Croissant
17
Toute construction de langage qui force une telle folie dans ce cas trivial devrait et peut être abandonnée sans regrets.
Erik Aronesty
86

Le problème avec le boost est que vous devez vous relier au boost et en dépendre. Pas facile dans certains cas (par exemple Android).

Et l'utilisation de char_traits signifie que toutes 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 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: Version Bonus 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);
                      });
}
Timmmm
la source
27
En fait, la bibliothèque de chaînes boost est une bibliothèque uniquement en-tête, il n'est donc pas nécessaire de créer de lien vers quoi que ce soit. En outre, vous pouvez utiliser l'utilitaire 'bcp' de boost pour copier uniquement les en-têtes de chaîne dans votre arborescence source, vous n'avez donc pas besoin d'avoir besoin de la bibliothèque de boost complète.
Gretchen
Ah je ne connaissais pas bcp, ça a l'air vraiment utile. Merci pour l'info!
Timmmm
9
Bon à savoir une version simple et sans dépendance de boost.
Deqing
2
La bibliothèque @Anna Text de boost doit être créée et reliée. Il utilise IBM ICU.
Behrouz.M
Également disponible avec C ++ 11
martian
58

Si vous êtes sur un système POSIX, vous pouvez utiliser strcasecmp . Cette fonction ne fait pas partie de la norme C, cependant, elle n'est pas non plus disponible sur Windows. Cela effectuera une comparaison insensible à la casse sur les caractères 8 bits, tant que les paramètres régionaux sont POSIX. Si les paramètres régionaux ne sont pas POSIX, les résultats ne sont pas définis (il peut donc effectuer une comparaison localisée ou non). Un équivalent à caractères larges n'est pas disponible.

A défaut, un grand nombre d'implémentations de bibliothèque C historiques ont les fonctions stricmp () et strnicmp (). Visual C ++ sur Windows a renommé tous ces éléments en les préfixant avec un trait de soulignement car 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 également avoir des fonctions équivalentes à caractères larges ou à plusieurs octets (généralement nommées par exemple wcsicmp, mbcsicmp, etc.).

C et C ++ sont tous deux largement ignorants des problèmes d'internationalisation, il n'y a donc pas de bonne solution à ce problème, sauf pour utiliser une bibliothèque tierce. Consultez IBM ICU (International Components for Unicode) si vous avez besoin d'une bibliothèque robuste pour C / C ++. ICU est destiné aux systèmes Windows et Unix.

Parc Derek
la source
53

Parlez-vous d'une comparaison insensible à la casse ou d'une comparaison Unicode normalisée complète?

Une comparaison stupide ne trouvera pas de chaînes qui pourraient être identiques mais qui ne sont pas égales en binaire.

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 ont également des représentations binaires différentes.

Cela dit, la normalisation Unicode devrait être une lecture obligatoire, surtout si vous prévoyez de prendre en charge le Hangul, le Thaï et d'autres langues asiatiques.

De plus, IBM a à peu près breveté les algorithmes Unicode les plus optimisés et les a rendus publics. Ils maintiennent également une implémentation: IBM ICU

Coincoin
la source
2
Vous voudrez peut-être modifier ce lien ICU vers site.icu-project.org
DevSolar
31

boost :: iequals n'est pas compatible utf-8 dans le cas d'une chaîne. Vous pouvez utiliser boost :: locale .

comparator<char,collator_base::secondary> cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;
  • Primaire - ignore les accents et la casse des caractères, en comparant uniquement les lettres de base. Par exemple, "façade" et "Façade" sont les mêmes.
  • Secondaire - ignorer la casse des caractères mais considérer les accents. "façade" et "façade" sont différents mais "Façade" et "façade" sont les mêmes.
  • Tertiaire - considérez à la fois le boîtier et les accents: "Façade" et "façade" sont différents. Ignorez la ponctuation.
  • Quaternaire - tenez compte de tous les cas, accents et signes de ponctuation. Les mots doivent être identiques en termes de représentation Unicode.
  • Identique - comme quaternaire, mais comparez également les points de code.
Igor Milyakov
la source
30

Ma première pensée pour une version non-unicode a été 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;
}
Shadow2531
la source
20

Vous pouvez utiliser strcasecmpsur Unix ou stricmpsur 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, car ces informations sont déjà disponibles dans la classe de chaînes. Cela pourrait empêcher de faire la comparaison de chaînes coûteuse si les deux chaînes que vous comparez ne sont même pas de la même longueur en premier lieu.

bradtgmurray
la source
Étant donné que la détermination de la longueur d'une chaîne consiste à itérer sur chaque caractère de la chaîne et à la comparer à 0, y a-t-il vraiment autant de différence entre cela et simplement comparer les chaînes immédiatement? Je suppose que vous obtenez une meilleure localité de mémoire dans le cas où les deux chaînes ne correspondent pas, mais probablement près de 2 fois l'exécution en cas de correspondance.
uliwitness
3
C ++ 11 spécifie que la complexité de std :: string :: length doit être constante: cplusplus.com/reference/string/string/length
bradtgmurray
1
C'est un petit fait amusant, mais qui a peu d'incidence ici. strcasecmp () et stricmp () prennent tous les deux des chaînes C non décorées, donc il n'y a pas de chaîne std :: impliquée.
uliwitness
3
Ces méthodes renverront -1 si vous comparez "a" vs "ab". Les longueurs sont différentes mais "a" précède "ab". Ainsi, la simple comparaison des longueurs n'est pas possible si l'appelant se soucie de la commande.
Nathan
14

Fonctions de chaîne Visual C ++ prenant en charge unicode: http://msdn.microsoft.com/en-us/library/cc194799.aspx

celui que vous cherchez probablement est _wcsnicmp

Darren Kopp
la source
7
Ironiquement, les «codes de caractères larges» de Microsoft ne sont PAS propres à unicode car ils ne gèrent pas la normalisation unicode.
vy32
13

J'essaie de concocter une bonne réponse de tous les messages, alors aidez-moi à modifier ceci:

Voici une méthode pour le faire, même si elle transforme les chaînes et n'est pas compatible avec Unicode, elle devrait être portable, ce qui est un avantage:

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 () car stricmp () ne fait en fait pas partie de la bibliothèque std, mais seulement implémenté par la plupart des éditeurs de compilateurs.

Pour obtenir une implémentation vraiment Unicode, il semble que vous devez sortir de la bibliothèque std. Une bonne bibliothèque tierce est l' IBM ICU (International Components for Unicode)

Aussi boost :: iequals fournit un assez bon utilitaire pour faire ce genre de comparaison.

Adam
la source
pouvez-vous dire, qu'est-ce que cela signifie :: tolower, pourquoi vous pouvez utiliser tolower au lieu de tolower (), et qu'est-ce que '::' avant? merci
VextoR
17
Ce n'est pas une solution très efficace - vous faites des copies des deux chaînes et vous les transformez toutes même si le premier caractère est différent.
Timmmm
2
Si vous voulez quand même en faire une copie, pourquoi ne pas passer par valeur plutôt que par référence?
celticminstrel
Je pense que c'est une astuce simple sans boost. :)
cmcromance
1
la question demande explicitement de ne pas transformla chaîne entière avant la comparaison
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);})

Vous pouvez utiliser le code ci-dessus en C ++ 14 si vous n'êtes pas en mesure d'utiliser boost. Vous devez utiliser std::towlowerpour les caractères larges.

vigne
la source
4
Je pense que vous devez ajouter un str1.size() == str2.size() &&à l'avant afin que cela ne sorte pas des limites lorsque str2 est un préfixe de str1.
ɲeuroburɳ
11

La bibliothèque Boost.String possède de nombreux algorithmes pour effectuer des comparaisons insensibles à la casse, etc.

Vous pouvez implémenter le vôtre, mais pourquoi s'embêter quand c'est déjà fait?

Dean Harding
la source
1
Il n'y a pas de moyen intégré avec std :: string?
WilliamKF
6
Non, il n'y en a pas.
Dean Harding
3
"... pourquoi s'embêter quand c'est déjà fait?" - que faire si vous n'utilisez pas Boost? L'OP n'avait pas l'étiquette avec la question.
2016
11

Pour info, strcmp()et stricmp()sont vulnérables au débordement de tampon, car ils ne traitent que jusqu'à ce qu'ils atteignent un terminateur nul. Il est plus sûr d'utiliser _strncmp()et _strnicmp().

Coin
la source
6
Vrai, bien que la sur-lecture d'un tampon soit beaucoup moins dangereuse que la surécriture d'un tampon.
Adam Rosenfield
4
stricmp()et strnicmp()ne font pas partie de la norme POSIX :-( Cependant , vous pouvez trouver strcasecmp(), strcasecmp_l(), strncasecmp()etstrncasecmp_l() l' en - tête Posix strings.h:-) voir opengroup.org
olibre
2
@AdamRosenfield 'pire' dépend du contexte. Dans le domaine de la sécurité, parfois le but d'un écrasement est d'arriver à dépasser.
karmakaze
10

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

Démo

Brian Rodriguez
la source
1
Cette méthode est potentiellement dangereuse et non portable. std::tolowerne fonctionne que si le caractère est codé en ASCII. Il n'y a pas de telle garantie pour std::string- il peut donc s'agir facilement d'un comportement indéfini.
plasmacel
@plasmacel Ensuite, utilisez une fonction qui fonctionne avec d'autres encodages.
Brian Rodriguez
9

Pour mes besoins de comparaison de chaînes insensibles à la casse, je préfère ne pas avoir à utiliser une bibliothèque externe, ni une classe de chaîne distincte avec des traits insensibles à la casse qui est incompatible avec toutes mes autres chaînes.

Donc, ce que j'ai trouvé, c'est ceci:

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 ça devrait aller sur n'importe quelle plateforme.

La comparaison d'égalité ne prendra pas en compte les problèmes tels que l'encodage de longueur variable et la normalisation Unicode, mais basic_string n'a aucun support pour cela que je sache de toute façon et ce n'est normalement pas un problème.

Dans les cas où une manipulation lexicographique plus sophistiquée du texte est requise, il vous suffit d'utiliser une bibliothèque tierce comme Boost, ce qui est normal.

Neutrino
la source
2
Vous pourriez probablement créer cette fonction si vous en faisiez un modèle et utilisiez basic_string <T> au lieu de versions séparées chaîne / chaîne de caractères?
uliwitness
2
Comment le modèle de fonction unique invoquerait-il Toupper ou Towupper sans recourir à la spécialisation ou aux macros, une surcharge de fonction semble être une implémentation plus simple et plus appropriée que l'une ou l'autre.
Neutrino
9

Court et agréable. Pas d'autres dépendances, que la bibliothèque std C standard étendue .

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

renvoie vrai si str1et str2sont égaux. strcasecmppeut ne pas exister, il pourrait y avoir des analogues stricmp, strcmpietc.

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

Production:

true
true
true
true
true
kyb
la source
6
il est étrange que C ++ std :: string n'ait pas de méthode de comparaison de casse
ignorée
1
"strcasecmp ne fait pas partie de la norme" - Mark Ransom 1 décembre 14 à 19:57
Liviu
oui, mais la plupart des compilateurs modernes l'ont ou son autre nom analogique. stricmp, strcmpi, strcasecmp, Etc. Je vous remercie. message modifié.
kyb
TODO: utilisez cout << boolalphaplutôt que my bool2strcar It pour convertir implicitement bool en chars pour stream.
kyb
C'est dans <strings.h> dans les bibliothèques de gcc.
Owl
7

Faire cela 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 
}
DavidS
la source
6

En supposant que vous recherchez une méthode et non une fonction magique qui existe déjà, il n'y a franchement pas de meilleur moyen. Nous pourrions tous écrire des extraits de code avec des astuces intelligentes pour des jeux de caractères limités, mais à la fin de la journée, à un moment donné, vous devez convertir les caractères.

La meilleure approche pour cette conversion est de le faire avant la comparaison. Cela vous permet une grande flexibilité en ce qui concerne les schémas de codage, ce que votre opérateur de comparaison réel devrait ignorer.

Vous pouvez bien sûr «masquer» cette conversion derrière votre propre fonction ou classe de chaîne, mais vous devez toujours convertir les chaînes avant la comparaison.

Andrew Grant
la source
6

J'ai écrit une version insensible à la casse de char_traits à utiliser avec std :: basic_string afin de générer une chaîne std :: qui ne respecte pas la casse lors des comparaisons, des recherches, etc. à l'aide des fonctions membres std :: basic_string intégrées.

En d'autres termes, je voulais faire quelque chose comme ça.

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

assert( a == b );

... que std :: string ne peut pas gérer. Voici l'utilisation de mes nouveaux char_traits:

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

assert( a == b );

... et voici l'implémentation:

/*  ---

        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;
John Dibling
la source
2
Cela fonctionne pour les caractères normaux, mais ne fonctionnera pas pour tout Unicode, car la légitimation n'est pas nécessairement bidirectionnelle (il y a un bon exemple en grec impliquant sigma dont je ne me souviens pas pour le moment; quelque chose comme ça a deux minuscules et un majuscule , et vous ne pouvez pas obtenir une comparaison correcte dans les deux cas)
coppro
1
C'est vraiment la mauvaise façon de procéder. La sensibilité à la casse ne doit pas être une propriété des chaînes elles-mêmes. Que se passe-t-il lorsque le même objet chaîne a besoin de comparaisons sensibles à la casse et à la casse?
Ferruccio
Si la sensibilité à la casse n'est pas appropriée pour faire "partie" de la chaîne, la fonction find () ne l'est pas non plus. Ce qui, pour vous, pourrait être vrai, et ça va. IMO la plus grande chose à propos de C ++ est qu'il ne force pas un paradigme particulier sur le programmeur. C'est ce que vous voulez / avez besoin que ce soit.
John Dibling
En fait, je pense que la plupart des C ++ - les gourous (comme ceux du comité des normes) conviennent que c'était une erreur de mettre find () dans std :: basic_string <> avec beaucoup d'autres choses qui pourraient tout aussi bien être placées dans fonctions libres. De plus, il y a quelques problèmes à le mettre dans le type.
Andreas Magnusson,
Comme d'autres l'ont souligné, il y a deux problèmes majeurs avec cette solution (ironiquement, l'un est l'interface et l'autre est l'implémentation ;-)).
Konrad Rudolph
4

J'ai une bonne expérience de l'utilisation du bibliothèques International Components for Unicode - elles sont extrêmement puissantes et fournissent des méthodes pour la conversion, la prise en charge des paramètres régionaux, le rendu de la date et de l'heure, le mappage de casse (que vous ne semblez pas vouloir) et le classement , qui inclut la comparaison insensible à la casse et à l'accent (et plus). Je n'ai utilisé que la version C ++ des bibliothèques, mais elles semblent également avoir une version Java.

Il existe des méthodes pour effectuer des comparaisons normalisées auxquelles fait référence @Coincoin, et peuvent même prendre en compte les paramètres régionaux - par exemple (et ceci est un exemple de tri, pas strictement égal), traditionnellement en espagnol (en Espagne), la combinaison de lettres "ll" trie entre "l" et "m", donc "lz" <"ll" <"ma".

Blair Conrad
la source
4

Utilisez-le simplement strcmp()pour la comparaison sensible à la casse et / strcmpi()ou stricmp()pour 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

Usage:

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;

Production

apple et ApPlE sont les mêmes

a vient avant b, donc la pomme vient avant la balle

reubenjohn
la source
2
Downvote parce que ce n'est guère une façon de faire les choses en C ++.
Thomas Daugaard
Il s'agit de la convention c ++ dans mon université, mais je
m'en souviendrai
4
stricmp est une extension Microsoft AFAIK. BSD semble avoir strcasecmp () à la place.
uliwitness
3

Tard dans la soirée, mais voici une variante qui utilise std::locale, et gère donc correctement le turc:

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

vous donne un foncteur qui utilise les paramètres régionaux actifs pour convertir les caractères en minuscules, que vous pouvez ensuite utiliser via std::transformpour générer des chaînes en minuscules:

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

Cela fonctionne également pour les wchar_tchaînes basées.

Simon Richter
la source
2

Juste une note sur la méthode que vous choisissez finalement, si cette méthode inclut l'utilisation de strcmp cela, certaines réponses suggèrent:

strcmpne fonctionne pas avec les données Unicode en général. En général, cela ne fonctionne même pas avec les encodages Unicode basés sur les octets, tels que utf-8, car strcmpseules les comparaisons octet par octet et les points de code Unicode encodés dans utf-8 peuvent prendre plus d'un octet. Le seul cas Unicode spécifique strcmpà gérer correctement est lorsqu'une chaîne codée avec un codage basé sur octets ne contient que des points de code inférieurs à U + 00FF - alors la comparaison octet par octet est suffisante.

Johann Gerell
la source
2

Début 2013, le projet ICU, maintenu par IBM, est une assez bonne réponse à cela.

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

ICU est une «bibliothèque Unicode complète et portable qui suit de près les normes de l'industrie». Pour le problème spécifique de la comparaison de 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 pouvez suivre la discussion d'ingénierie, y compris les problèmes de génération de systèmes et de taille de fichier de données, ici:

michaelhanson
la source
2

Il semble que les solutions ci-dessus n'utilisent pas la méthode de comparaison et n'implémentent à nouveau le total, alors voici ma solution et j'espère que cela fonctionne pour vous (cela fonctionne 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;
}
Jagadeesh Pulamarasetti
la source
1

Si vous ne voulez pas utiliser la bibliothèque Boost, voici la solution en utilisant uniquement l'en-tête io standard 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;
}
HaSeeB MiR
la source
Je crois que std :: toupper est dans #include <cctype>, vous devrez peut-être l'inclure.
David Ledger
Si vous utilisez une version globale comme celle-ci :: toupper, vous n'aurez peut-être pas besoin d'inclure <ctype> car il existe deux versions, la version c et la version c ++ avec les paramètres régionaux, je suppose. Il vaut donc mieux utiliser la version globale ":: toupper ()"
HaSeeB MiR
cette solution échoue lorsque l'une des chaînes est vide: "" - elle retourne true dans ce cas où elle doit retourner false
ekkis
0

Si vous devez comparer une chaîne source plus souvent avec d'autres chaînes, une solution élégante consiste à utiliser l'expression régulière.

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

std::wregex pattern(first, std::wregex::icase);
bool isEqual = std::regex_match(second, pattern);
smibe
la source
J'ai essayé ceci mais compiler une erreur: error: conversion from 'const char [5]' to non-scalar type 'std::wstring {aka std::basic_string<wchar_t>}' requested
Deqing
mauvaise idée. C'est la pire solution.
Behrouz.M
Ce n'est pas une bonne solution, mais même si vous vouliez l'utiliser, vous avez besoin d'un L devant vos constantes de chaînes plus larges, par exemple L "TEST"
celticminstrel
Ce serait bien si quelqu'un pouvait expliquer pourquoi c'est la pire solution. En raison de problèmes de performances? La création de l'expression régulière coûte cher, mais la comparaison devrait ensuite être très rapide.
smibe
il est utilisable et portable, le problème majeur est que le premier ne peut contenir aucun caractère utilisé par regex. Il ne peut pas être utilisé comme comparaison de chaîne générale à cause de cela. Il sera également plus lent, il y a un indicateur pour le faire fonctionner comme le dit smibe mais ne peut toujours pas être utilisé comme fonction générale.
Ben
0

Un moyen simple de comparer deux chaînes en c ++ (testé pour Windows) utilise _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

Dame
la source
Cela vaut la peine de lire stackoverflow.com/a/12414441/95309 en plus de cette réponse, car c'est a) une fonction C, et b) soi-disant pas portable.
Claus Jørgensen
De quoi avons-nous besoin pour que cela fonctionne?
ekkis
1
@ekkis pour utiliser _stricmp, vous devez inclure <string.h> comme vous pouvez le lire ici: docs.microsoft.com/en-us/cpp/c-runtime-library/reference/…
DAme
-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;
        }
      }
}

cela pourrait probablement être rendu beaucoup plus efficace, mais voici une version volumineuse avec tous ses bits à nu.

pas tout à fait portable, mais fonctionne bien avec tout ce qui est sur mon ordinateur (aucune idée, je suis des images pas des mots)

user4578093
la source
Ce n'est pas le support Unicode, c'est ce que la question a posé.
Behrouz.M
Cela ne prend pas en charge les jeux de caractères non anglais.
Robert Andrzejuk
-3

Un moyen simple de comparer des chaînes qui ne diffèrent que par des minuscules et des majuscules consiste à effectuer une comparaison ascii. Toutes les lettres majuscules et minuscules diffèrent de 32 bits dans le tableau ascii, en utilisant ces informations, nous avons ce qui suit ...

    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
    }
}
Craig Stoddard
la source
3
Selon cela, "++ j" sera trouvé égal à "KKJ" et "1234" sera égal à "QRST". Je doute que quelqu'un veuille quelque chose.
celticminstrel