Confronto di stringhe senza distinzione tra maiuscole e minuscole in C ++ [chiuso]


373

Qual è il modo migliore di fare un confronto di stringhe senza distinzione tra maiuscole e minuscole in C ++ senza trasformare una stringa in maiuscolo o minuscolo?

Indica se i metodi sono compatibili con Unicode e quanto sono portatili.


@ [Adam] (# 11679): Mentre questa variante è buona in termini di usabilità, è cattiva in termini di prestazioni perché crea copie non necessarie. Potrei trascurare qualcosa ma credo che il modo migliore (non Unicode) sia usare std::stricmp. Altrimenti, leggi cosa ha da dire Herb .
Konrad Rudolph,

In c, di solito uno è stato costretto a fornire l'intera stringa, quindi confrontare in quel modo - o eseguire il tuo confronto: P
Michael Dorgan,

una domanda in seguito ha una risposta semplice: strcasecmp (almeno per BSD e POSIX compilatori) stackoverflow.com/questions/9182912/...
Moz

@ Mσᶎ questa domanda ha anche quella risposta, con l'importante avvertimento che strcasecmpnon fa parte dello standard e manca da almeno un compilatore comune.
Mark Ransom,

Risposte:


318

Boost include un utile algoritmo per questo:

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

14
Questo UTF-8 è amichevole? Penso di no.
Vladr,

18
No, perché UTF-8 consente di codificare stringhe identiche con codici binari diversi, a causa di accenti, combinazioni, problemi di bidi, ecc.
vy32,

10
@ vy32 Questo è assolutamente errato! Le combinazioni UTF-8 si escludono a vicenda. Deve sempre utilizzare la rappresentazione più breve possibile, in caso contrario, è una sequenza o un punto di codice UTF-8 non valido che deve essere trattato con cura.
Wiz,

48
@Wiz, stai ignorando il problema della normalizzazione della stringa Unicode. ñ può essere rappresentato come una combinazione ˜ seguito da una n o con un carattere ñ. È necessario utilizzare la normalizzazione della stringa Unicode prima di eseguire il confronto. Si prega di consultare il rapporto tecnico Unicode n. 15, unicode.org/reports/tr15
vy32

12
@wonkorealtime: perché "ß" convertito in maiuscolo è "SS": fileformat.info/info/unicode/char/df/index.htm
Mooing Duck

118

Approfitta dello standard char_traits. Ricordiamo che una std::stringè in realtà un typedef per std::basic_string<char>, o più esplicitamente, std::basic_string<char, std::char_traits<char> >. Il char_traitstipo descrive come i personaggi si confrontano, come si copiano, come si lanciano, ecc. Tutto quello che devi fare è digitare una nuova stringa basic_stringe fornirla con la tua personalizzazione char_traitsche confronta il caso in modo insensibile.

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;

I dettagli sono su Guru of The Week numero 29 .


10
Per quanto ne so dalla mia stessa sperimentazione, questo rende il tuo nuovo tipo di stringa incompatibile con std :: string.
Zan Lynx,

8
Certo che lo fa - per il suo bene. Una stringa senza distinzione tra maiuscole e minuscole è qualcos'altro: typedef std::basic_string<char, ci_char_traits<char> > istringno typedef std::basic_string<char, std::char_traits<char> > string.
Andreas Spindler,

232
"Tutto quello che devi fare ..."
Tim MB

3
@Nathan probabilmente usa un compilatore in grado di eseguire CSE di base sul codice ...
The Paramagnetic Croissant,

17
Qualsiasi costrutto linguistico che imponga tale follia in questo banale caso dovrebbe e può essere abbandonato senza rimpianti.
Erik Aronesty,

86

Il problema con boost è che devi collegarti e dipendere da boost. In alcuni casi non è facile (ad es. Android).

E l'utilizzo di char_traits significa che tutti i tuoi confronti non fanno distinzione tra maiuscole e minuscole, che di solito non è quello che desideri.

Questo dovrebbe bastare. Dovrebbe essere ragionevolmente efficiente. Non gestisce Unicode o altro.

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

Aggiornamento: Bonus C ++ 14 versione ( #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);
                      });
}

27
In realtà, la libreria di stringhe boost è una libreria solo intestazione, quindi non è necessario collegarsi a nulla. Inoltre, puoi usare l'utility 'bcp' di boost per copiare solo le intestazioni di stringa nella tua struttura di origine, quindi non è necessario richiedere la libreria di boost completa.
Gretchen,

Ah, non sapevo di bcp, sembra davvero utile. Grazie per le informazioni!
Timmmm,

9
Buono a sapersi una versione semplice e senza dipendenza.
Deqing,

2
@Anna È necessario creare e collegare una libreria di testo di boost. Utilizza IBM ICU.
Behrouz.M,

Disponibile anche con C ++ 11
martian

58

Se si utilizza un sistema POSIX, è possibile utilizzare strcasecmp . Questa funzione non fa parte dello standard C, tuttavia, né è disponibile su Windows. Ciò eseguirà un confronto senza distinzione tra maiuscole e minuscole su caratteri a 8 bit, purché la locale sia POSIX. Se la locale non è POSIX, i risultati non sono definiti (quindi potrebbe fare un confronto localizzato o potrebbe non farlo). Un equivalente di carattere largo non è disponibile.

In caso contrario, un gran numero di implementazioni di librerie C storiche hanno le funzioni stricmp () e strnicmp (). Visual C ++ su Windows ha rinominato tutti questi prefissandoli con un carattere di sottolineatura perché non fanno parte dello standard ANSI, quindi su quel sistema sono chiamati _stricmp o _strnicmp . Alcune librerie possono anche avere funzioni equivalenti a più caratteri o multibyte (in genere denominate ad esempio wcsicmp, mbcsicmp e così via).

C e C ++ sono entrambi in gran parte ignari dei problemi di internazionalizzazione, quindi non esiste una buona soluzione a questo problema, se non l'uso di una libreria di terze parti. Dai un'occhiata a IBM ICU (International Components for Unicode) se hai bisogno di una libreria robusta per C / C ++. ICU è per sistemi Windows e Unix.


53

Stai parlando di un confronto insensibile alle maiuscole o di un confronto Unicode completamente normalizzato?

Un confronto stupido non troverà stringhe che potrebbero essere uguali ma che non sono binarie uguali.

Esempio:

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

Sono tutti equivalenti ma hanno anche diverse rappresentazioni binarie.

Detto questo, la normalizzazione Unicode dovrebbe essere una lettura obbligatoria soprattutto se hai intenzione di supportare Hangul, Thaï e altre lingue asiatiche.

Inoltre, IBM ha praticamente brevettato gli algoritmi Unicode ottimizzati e li ha resi disponibili al pubblico. Mantengono anche un'implementazione: IBM ICU


2
Potresti voler modificare quel link ICU a site.icu-project.org
DevSolar il

31

boost :: iequals non è compatibile utf-8 nel caso di stringhe. Puoi usare boost :: locale .

comparator<char,collator_base::secondary> cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;
  • Primario: ignora gli accenti e il caso del carattere, confrontando solo le lettere di base. Ad esempio "facciata" e "facciata" sono uguali.
  • Secondario: ignora il caso del personaggio ma considera gli accenti. "facciata" e "facciata" sono diverse ma "Facciata" e "facciata" sono uguali.
  • Terziario - considera sia il caso che gli accenti: "Facciata" e "facciata" sono diversi. Ignora la punteggiatura.
  • Quaternario: considera tutti i casi, gli accenti e la punteggiatura. Le parole devono essere identiche in termini di rappresentazione Unicode.
  • Identico - come quaternario, ma confronta anche i punti di codice.

30

Il mio primo pensiero per una versione non unicode è stato quello di fare qualcosa del genere:


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

20

È possibile utilizzare strcasecmpsu Unix o stricmpsu Windows.

Una cosa che non è stata menzionata finora è che se si utilizzano stringhe stl con questi metodi, è utile prima confrontare la lunghezza delle due stringhe, poiché queste informazioni sono già disponibili nella classe stringa. Ciò potrebbe impedire di effettuare il costoso confronto delle stringhe se le due stringhe che si stanno confrontando non sono nemmeno della stessa lunghezza in primo luogo.


Dato che determinare la lunghezza di una stringa consiste nell'iterare su ogni carattere della stringa e confrontarlo con 0, c'è davvero tanta differenza tra quello e confrontare le stringhe subito? Suppongo che otterrai una migliore localizzazione della memoria nel caso in cui entrambe le stringhe non corrispondano, ma probabilmente quasi 2x l'autonomia in caso di una corrispondenza.
uliwitness

3
C ++ 11 specifica che la complessità di std :: string :: length deve essere costante: cplusplus.com/reference/string/string/length
bradtgmurray

1
Questo è un piccolo fatto divertente, ma ha poca rilevanza qui. strcasecmp () e stricmp () accettano entrambi stringhe C non decorate, quindi non è coinvolto std :: string.
uliwitness,

3
Questi metodi restituiranno -1 se si confronta "a" vs "ab". Le lunghezze sono diverse ma "a" precede "ab". Pertanto, il semplice confronto delle lunghezze non è possibile se il chiamante si preoccupa di ordinare.
Nathan,


13

Sto cercando di mettere insieme una buona risposta da tutti i post, quindi aiutami a modificare questo:

Ecco un metodo per farlo, anche se trasforma le stringhe e non è compatibile con Unicode, dovrebbe essere portatile, il che è un vantaggio:

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

Da quello che ho letto, questo è più portatile di stricmp () perché stricmp () non è in realtà parte della libreria std, ma è implementato solo dalla maggior parte dei produttori di compilatori.

Per ottenere un'implementazione davvero amichevole Unicode sembra che tu debba andare fuori dalla libreria std. Una buona libreria di terze parti è la IBM ICU (International Components for Unicode)

Anche boost :: iequals fornisce un'utilità abbastanza buona per fare questo tipo di confronto.


puoi per favore dire cosa significa :: tolower, perché puoi usare tolower invece di tolower (), e prima cosa è "::"? grazie
VextoR

17
Questa non è una soluzione molto efficiente: fai copie di entrambe le stringhe e le trasformi tutte anche se il primo carattere è diverso.
Timmmm,

2
Se hai intenzione di fare una copia comunque, perché non passare per valore invece che per riferimento?
celticminstrel,

Penso che sia semplice suggerimento senza boost. :)
cmcromance

1
la domanda chiede esplicitamente di non transforml'intera stringa prima del confronto
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);})

È possibile utilizzare il codice sopra riportato in C ++ 14 se non si è in grado di utilizzare boost. Devi usare std::towlowerper caratteri ampi.


4
Penso che sia necessario aggiungere a str1.size() == str2.size() &&in modo che non esca dai limiti quando str2 è un prefisso di str1.
ɲeuroburɳ

11

La libreria Boost.String ha molti algoritmi per fare confronti senza distinzione tra maiuscole e minuscole e così via.

Potresti implementare il tuo, ma perché preoccuparsi quando è già stato fatto?


1
Non c'è un modo integrato con std :: string?
WilliamKF,

6
No, non c'è.
Dean Harding,

3
"... perché preoccuparsi quando è già stato fatto?" - cosa succede se non si utilizza Boost? L'OP non aveva il tag con la domanda.
jww

11

Cordiali saluti, strcmp()e stricmp()sono vulnerabili al buffer overflow, poiché elaborano solo fino a quando non raggiungono un terminatore null. È più sicuro da usare _strncmp()e _strnicmp().


6
È vero, sebbene l'overREAD di un buffer sia significativamente meno pericoloso rispetto all'overWRITE di un buffer.
Adam Rosenfield,

4
stricmp()e strnicmp()non sono parte dello standard POSIX :-( Tuttavia si possono trovare strcasecmp(), strcasecmp_l(), strncasecmp()e strncasecmp_l()nell'intestazione POSIX strings.h:-) vedi opengroup.org
olibre

2
@AdamRosenfield "peggio" dipende dal contesto. In termini di sicurezza, a volte il punto cruciale di una sovrascrittura è quello di arrivare alla lettura eccessiva.
karmakaze,

10

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

dimostrazione


1
Questo metodo è potenzialmente pericoloso e non portatile. std::tolowerfunziona solo se il carattere è codificato ASCII. Non esiste una tale garanzia per std::string- quindi può essere facilmente un comportamento indefinito.
Plasmacel,

@plasmacel Quindi utilizzare una funzione che funziona con altre codifiche.
Brian Rodriguez,

9

Per le mie esigenze di confronto di stringhe senza distinzione tra maiuscole e minuscole preferisco non dover utilizzare una libreria esterna, né desidero una classe di stringhe separata con tratti insensibili alle maiuscole e incompatibili con tutte le altre stringhe.

Quindi quello che ho escogitato è questo:

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 semplice funzione con un sovraccarico per char e un altro per whar_t. Non usa nulla di non standard quindi dovrebbe andare bene su qualsiasi piattaforma.

Il confronto sull'uguaglianza non prenderà in considerazione problemi come la codifica a lunghezza variabile e la normalizzazione Unicode, ma basic_string non ha alcun supporto per ciò di cui sono a conoscenza e che normalmente non rappresenta un problema.

Nei casi in cui è richiesta una manipolazione lessicografica più sofisticata del testo, devi semplicemente utilizzare una libreria di terze parti come Boost, che è prevedibile.


2
Probabilmente potresti fare quella funzione se lo rendessi un modello e usi basic_string <T> invece di versioni separate stringa / wstring?
uliwitness

2
In che modo il modello a singola funzione invocherebbe toupper o towupper senza ricorrere all'uso di specializzazioni o macro, un sovraccarico di funzioni sembra un'implementazione più semplice e più appropriata di entrambe.
Neutrino,

9

Breve e carino Nessun'altra dipendenza, se non quella estesa std C lib.

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

restituisce vero se str1e str2sono uguali. strcasecmpnon può esistere, ci potrebbero essere analoghi stricmp, strcmpiecc

Codice di esempio:

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

Produzione:

true
true
true
true
true

6
è strano che C ++ std :: string non abbia un metodo di confronto tra maiuscole e minuscole.
kyb

1
"strcasecmp non fa parte dello standard" - Mark Ransom, 1 ° dicembre 14 alle 19:57
Liviu,

sì, ma la maggior parte dei compilatori moderni ce l'hanno o un altro analogo chiamato. stricmp, strcmpi, strcasecmp, Ecc Grazie. messaggio modificato.
Kyb,

TODO: usa cout << boolalphapiuttosto che mio bool2strperché converte implicitamente il bool in caratteri per lo streaming.
Kyb

È in <strings.h> nelle librerie di gcc.
Gufo,

7

Puoi farlo senza usare Boost ottenendo il puntatore della stringa C c_str()e usando strcasecmp:

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

6

Supponendo che tu stia cercando un metodo e non una funzione magica che esiste già, francamente non c'è modo migliore. Tutti potremmo scrivere frammenti di codice con trucchi intelligenti per set di caratteri limitati, ma alla fine della giornata ad un certo punto devi convertire i caratteri.

L'approccio migliore per questa conversione è farlo prima del confronto. Ciò ti consente una grande flessibilità quando si tratta di codificare schemi, di cui il tuo operatore di confronto reale dovrebbe essere ignaro.

Ovviamente puoi 'nascondere' questa conversione dietro la tua funzione di stringa o classe, ma devi comunque convertire le stringhe prima del confronto.


6

Ho scritto una versione senza distinzione tra maiuscole e minuscole di char_traits da utilizzare con std :: basic_string per generare una stringa std :: che non distingue tra maiuscole e minuscole quando si effettuano confronti, ricerche, ecc. Utilizzando le funzioni membro std :: basic_string incorporate.

Quindi, in altre parole, volevo fare qualcosa del genere.

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

assert( a == b );

... che std :: string non può gestire. Ecco l'uso dei miei nuovi char_traits:

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

assert( a == b );

... ed ecco l'implementazione:

/*  ---

        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;

2
Questo funziona con caratteri regolari, ma non funzionerà per tutti Unicode, poiché la captitalizzazione non è necessariamente bidirezionale (c'è un buon esempio in greco che coinvolge sigma che non ricordo in questo momento; qualcosa del genere ha due lettere minuscole e una maiuscola e non è possibile ottenere un confronto adeguato in entrambi i modi)
coppro

1
Questo è davvero il modo sbagliato di farlo. La distinzione tra maiuscole e minuscole non dovrebbe essere una proprietà delle stringhe stesse. Cosa succede quando lo stesso oggetto stringa ha bisogno di confronti tra maiuscole e minuscole e maiuscole e minuscole?
Ferruccio,

Se la distinzione tra maiuscole e minuscole non è appropriata per essere "parte della" stringa, allora nemmeno la funzione find (). Il che, per te, potrebbe essere vero, e va bene. La cosa più importante dell'IMO nel C ++ è che non impone un particolare paradigma al programmatore. È quello che vuoi / ne hai bisogno.
John Dibling,

In realtà, penso che la maggior parte dei C ++ - i guru (come quelli del comitato degli standard) concordano sul fatto che sia stato un errore mettere find () in std :: basic_string <> insieme a molte altre cose che potrebbero essere ugualmente inserite funzioni gratuite. Inoltre ci sono alcuni problemi con l'inserimento nel tipo.
Andreas Magnusson,

Come altri hanno sottolineato, ci sono due cose principali sbagliate in questa soluzione (ironicamente, una è l'interfaccia e l'altra è l'implementazione ;-)).
Konrad Rudolph,

4

Ho avuto una buona esperienza nell'uso delle librerie International Components for Unicode : sono estremamente potenti e forniscono metodi di conversione, supporto locale, rendering di data e ora, mappatura dei casi (che non sembra voler) e fascicolazione , che include il confronto insensibile a maiuscole e minuscole (e altro). Ho usato solo la versione C ++ delle librerie, ma sembrano avere anche una versione Java.

Esistono metodi per eseguire confronti normalizzati come indicato da @Coincoin, e possono anche tenere conto delle impostazioni locali - ad esempio (e questo è un esempio di ordinamento, non strettamente uguaglianza), tradizionalmente in spagnolo (in Spagna), la combinazione di lettere "ll" ordina tra "l" e "m", quindi "lz" <"ll" <"ma".


4

Basta usare strcmp()per il confronto tra maiuscole e minuscole e / strcmpi()o maiuscole e stricmp()minuscole. Che sono entrambi nel file di intestazione<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;

Produzione

apple e ApPlE sono uguali

a viene prima di b, quindi la mela viene prima della palla


2
Downvote perché questo non è certo un modo C ++ di fare le cose.
Thomas Daugaard,

Questa è la convenzione c ++ nella mia università, ma la terrò a mente quando
invierò

4
stricmp è un'estensione Microsoft AFAIK. Invece BSD sembra avere strcasecmp ().
uliwitness

3

In ritardo alla festa, ma ecco una variante che utilizza std::localee quindi gestisce correttamente il turco:

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

ti dà un funzione che usa la locale attiva per convertire i caratteri in minuscolo, che puoi usare tramite std::transformper generare stringhe minuscole:

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

Questo funziona anche per le wchar_tstringhe basate.


2

Solo una nota su qualunque metodo tu scelga finalmente, se quel metodo include l'uso di strcmpquello che alcune risposte suggeriscono:

strcmpnon funziona con i dati Unicode in generale. In generale, non funziona nemmeno con le codifiche Unicode basate su byte, come utf-8, poiché strcmpsolo i confronti byte per byte e i punti di codice Unicode codificati in utf-8 possono richiedere più di 1 byte. L'unico caso Unicode specifico da strcmpgestire correttamente è quando una stringa codificata con una codifica basata su byte contiene solo punti di codice inferiori a U + 00FF - quindi il confronto byte per byte è sufficiente.


2

All'inizio del 2013, il progetto ICU, gestito da IBM, è una buona risposta a questo.

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

ICU è una "libreria Unicode completa e portatile che segue da vicino gli standard del settore". Per il problema specifico del confronto delle stringhe, l'oggetto Collation fa quello che vuoi.

Il progetto Mozilla ha adottato l'ICU per l'internazionalizzazione in Firefox a metà 2012; puoi tenere traccia della discussione di ingegneria, inclusi i problemi relativi ai sistemi di compilazione e alle dimensioni del file di dati, qui:


2

Sembra che le soluzioni di cui sopra non stiano utilizzando il metodo di confronto e implementando di nuovo il totale, quindi ecco la mia soluzione e spero che funzioni per te (Funziona bene).

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

1

Se non vuoi usare la libreria Boost, ecco una soluzione usando solo l'intestazione 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;
}

Credo che std :: toupper sia in #include <cctype>, potrebbe essere necessario includerlo.
David Ledger,

Se userete una versione globale come questa :: toupper, potreste non aver bisogno di includere <ctype> perché ci sono due versioni versione c e versione c ++ con locale credo. Quindi meglio usare la versione globale ":: toupper ()"
HaSeeB MiR

questa soluzione fallisce quando una delle stringhe è vuota: "" - restituisce true in quel caso quando dovrebbe restituire false
ekkis

0

Se devi confrontare una stringa sorgente più spesso con altre stringhe, una soluzione elegante è usare 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);

Ho provato questo errore di compilazione: error: conversion from 'const char [5]' to non-scalar type 'std::wstring {aka std::basic_string<wchar_t>}' requested
Deqing,

cattiva idea. È la peggior soluzione.
Behrouz.M,

Questa non è una buona soluzione, ma anche se volessi usarla, hai bisogno di una L davanti alle tue costanti allargate, ad esempio L "TEST"
celticminstrel,

Sarebbe bello se qualcuno potesse spiegare perché è la soluzione peggiore. A causa di problemi di prestazioni? La creazione di regex è costosa, ma in seguito il confronto dovrebbe essere molto veloce.
atmosfera

è utilizzabile e portatile, il problema principale è che in primo luogo non può contenere alcun carattere utilizzato da regex. Non può essere usato come confronto di stringhe generali a causa di ciò. Sarà anche più lento, c'è una bandiera per farlo funzionare come dice l'atmosfera ma non può ancora essere usato come una funzione generale.
Ben

0

Un modo semplice per confrontare due stringhe in c ++ (testato per Windows) sta usando _stricmp

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

Se stai cercando di usare con std :: string, un esempio:

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

Per maggiori informazioni qui: https://msdn.microsoft.com/it-it/library/e0z9k731.aspx


Vale la pena leggere stackoverflow.com/a/12414441/95309 oltre a questa risposta, in quanto si tratta di a) una funzione C e b) presumibilmente non portatile.
Claus Jørgensen,

di quale #include abbiamo bisogno per farlo funzionare?
ekkis,

1
@ekkis per usare _stricmp devi includere <string.h> come puoi leggere qui: 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;
        }
      }
}

questo potrebbe probabilmente essere reso molto più efficiente, ma qui c'è una versione ingombrante con tutti i suoi bit nudi.

non così portatile, ma funziona bene con qualsiasi cosa sia sul mio computer (non ho idea, io sono di immagini e non di parole)


Questo non è il supporto Unicode, che è la domanda posta.
Behrouz.M,

Questo non supporta set di caratteri non inglesi.
Robert Andrzejuk,

-3

Un modo semplice per confrontare stringhe diverse solo per i caratteri minuscoli e maiuscoli è fare un confronto ASCII. Tutte le lettere maiuscole e minuscole differiscono di 32 bit nella tabella ASCII, utilizzando queste informazioni abbiamo i seguenti ...

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

3
Secondo questo, "++ j" sarà trovato uguale a "KKJ" e "1234" sarà trovato uguale a "QRST". Dubito che sia qualcosa che tutti vogliono.
celticminstrel,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.