String-Vergleich ohne Berücksichtigung der Groß- und Kleinschreibung in C ++ [geschlossen]

373

Was ist der beste Weg, um einen String-Vergleich ohne Berücksichtigung der Groß- und Kleinschreibung in C ++ durchzuführen, ohne einen String in Groß- oder Kleinbuchstaben umzuwandeln?

Bitte geben Sie an, ob die Methoden Unicode-freundlich sind und wie portabel sie sind.

Adam
quelle
@ [Adam] (# 11679): Obwohl diese Variante in Bezug auf die Benutzerfreundlichkeit gut ist, ist sie in Bezug auf die Leistung schlecht, da unnötige Kopien erstellt werden. Ich könnte etwas übersehen, aber ich glaube, der beste Weg (ohne Unicode) ist die Verwendung std::stricmp. Ansonsten lesen Sie, was Herb zu sagen hat .
Konrad Rudolph
In c war man normalerweise gezwungen, die gesamte Saite zu übersteigen und dann auf diese Weise zu vergleichen - oder einen eigenen Vergleich zu erstellen: P
Michael Dorgan
Eine spätere Frage hat eine einfachere Antwort: strcasecmp (zumindest für BSD- und POSIX-Compiler) stackoverflow.com/questions/9182912/…
Móż
@ Mσᶎ Diese Frage hat auch diese Antwort, mit der wichtigen Einschränkung, strcasecmpdie nicht Teil des Standards ist und in mindestens einem gemeinsamen Compiler fehlt.
Mark Ransom

Antworten:

317

Boost enthält hierfür einen praktischen Algorithmus:

#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
}
rauben
quelle
14
Ist das UTF-8 freundlich? Ich denke nicht.
Vladr
18
Nein, da UTF-8 die Codierung identischer Zeichenfolgen mit unterschiedlichen Binärcodes aufgrund von Akzenten, Kombinationen, Bidi-Problemen usw. ermöglicht
vy32
10
@ vy32 Das ist absolut falsch! Die UTF-8-Kombinationen schließen sich gegenseitig aus. Es muss immer die kürzestmögliche Darstellung verwendet werden. Ist dies nicht der Fall, handelt es sich um eine fehlerhafte UTF-8-Sequenz oder einen fehlerhaften Codepunkt, der mit Vorsicht behandelt werden muss.
Wiz
48
@Wiz, Sie ignorieren das Problem der Normalisierung von Unicode-Zeichenfolgen. ñ kann als Kombination ˜ gefolgt von einem n oder mit einem ñ-Zeichen dargestellt werden. Sie müssen die Unicode-String-Normalisierung verwenden, bevor Sie den Vergleich durchführen können. Bitte überprüfen Sie Unicode Technical Report # 15, unicode.org/reports/tr15
vy32
12
@wonkorealtime: weil "ß" in Großbuchstaben konvertiert ist "SS": fileformat.info/info/unicode/char/df/index.htm
Mooing Duck
118

Nutzen Sie den Standard char_traits. Denken Sie daran, dass a std::stringtatsächlich ein typedef für std::basic_string<char>oder expliziter ist std::basic_string<char, std::char_traits<char> >. Der char_traitsTyp beschreibt, wie Zeichen verglichen werden, wie sie kopiert werden, wie sie umgewandelt werden usw. Alles, was Sie tun müssen, ist, eine neue Zeichenfolge zu überschreiben basic_stringund sie mit Ihrer eigenen benutzerdefinierten Zeichenfolge zu versehen, bei der char_traitsGroß- und Kleinschreibung nicht berücksichtigt wird.

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;

Die Details sind auf Guru der Woche Nummer 29 .

wilhelmtell
quelle
10
Soweit ich aus meinen eigenen Experimenten weiß, ist Ihr neuer String-Typ dadurch nicht mit std :: string kompatibel.
Zan Lynx
8
Natürlich tut es das - zu seinem eigenen Besten. Eine Zeichenfolge, bei der die Groß- und Kleinschreibung nicht berücksichtigt wird, ist etwas anderes: typedef std::basic_string<char, ci_char_traits<char> > istringnicht typedef std::basic_string<char, std::char_traits<char> > string.
Andreas Spindler
232
"Alles was Sie tun müssen ..."
Tim MB
3
@ Nathan wahrscheinlich einen Compiler verwenden, der in der Lage ist, grundlegende CSE für den Code auszuführen ...
The Paramagnetic Croissant
17
Jedes Sprachkonstrukt, das in diesem trivialen Fall einen solchen Wahnsinn erzwingt, sollte und kann ohne Reue aufgegeben werden.
Erik Aronesty
86

Das Problem mit Boost ist, dass Sie sich mit Boost verbinden und von Boost abhängen müssen. In einigen Fällen nicht einfach (zB Android).

Die Verwendung von char_traits bedeutet, dass bei all Ihren Vergleichen die Groß- und Kleinschreibung nicht berücksichtigt wird. Dies ist normalerweise nicht das, was Sie möchten.

Das sollte ausreichen. Es sollte einigermaßen effizient sein. Behandelt jedoch keinen Unicode oder ähnliches.

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

Update: 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);
                      });
}
Timmmm
quelle
27
Tatsächlich ist die Boost-String-Bibliothek eine reine Header-Bibliothek, sodass keine Verknüpfung zu irgendetwas erforderlich ist. Sie können auch das Dienstprogramm 'bcp' von boost verwenden, um nur die Zeichenfolgenheader in Ihren Quellbaum zu kopieren, sodass Sie nicht die vollständige Boost-Bibliothek benötigen.
Gretchen
Ah, ich wusste nichts über bcp, es sieht wirklich nützlich aus. Danke für die Information!
Timmmm
9
Gut zu wissen, eine einfache und nicht Boost-abhängige Version.
Deqing
2
@Anna Textbibliothek von Boost muss erstellt und verknüpft werden. Es verwendet IBM ICU.
Behrouz.M
Auch verfügbar mit C ++ 11
Mars
58

Wenn Sie sich auf einem POSIX-System befinden, können Sie strcasecmp verwenden . Diese Funktion ist jedoch weder Teil von Standard C noch unter Windows verfügbar. Dadurch wird ein Vergleich ohne Berücksichtigung der Groß- und Kleinschreibung für 8-Bit-Zeichen durchgeführt, sofern das Gebietsschema POSIX ist. Wenn das Gebietsschema nicht POSIX ist, sind die Ergebnisse undefiniert (daher wird möglicherweise ein lokalisierter Vergleich durchgeführt oder nicht). Ein Breitzeichenäquivalent ist nicht verfügbar.

Andernfalls haben viele historische C-Bibliotheksimplementierungen die Funktionen stricmp () und strnicmp (). Visual C ++ unter Windows hat all dies umbenannt, indem ihnen ein Unterstrich vorangestellt wurde, da sie nicht Teil des ANSI-Standards sind. Auf diesem System heißen sie also _stricmp oder _strnicmp . Einige Bibliotheken verfügen möglicherweise auch über Breitzeichen- oder Multibyte-äquivalente Funktionen (normalerweise z. B. wcsicmp, mbcsicmp usw.).

C und C ++ kennen beide Internationalisierungsprobleme weitgehend nicht, daher gibt es keine gute Lösung für dieses Problem, außer die Verwendung einer Bibliothek eines Drittanbieters. Schauen Sie sich IBM ICU (International Components for Unicode) an, wenn Sie eine robuste Bibliothek für C / C ++ benötigen. Die Intensivstation ist sowohl für Windows- als auch für Unix-Systeme geeignet.

Derek Park
quelle
53

Sprechen Sie über einen dummen Vergleich ohne Berücksichtigung der Groß- und Kleinschreibung oder einen vollständig normalisierten Unicode-Vergleich?

Bei einem dummen Vergleich werden keine Zeichenfolgen gefunden, die möglicherweise gleich, aber nicht binär gleich sind.

Beispiel:

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

Sind alle gleichwertig, haben aber auch unterschiedliche binäre Darstellungen.

Die Unicode-Normalisierung sollte jedoch obligatorisch sein, insbesondere wenn Sie Hangul, Thaï und andere asiatische Sprachen unterstützen möchten.

Außerdem hat IBM die am meisten optimierten Unicode-Algorithmen patentiert und öffentlich zugänglich gemacht. Sie pflegen auch eine Implementierung: IBM ICU

Münze
quelle
2
Vielleicht möchten Sie diesen ICU-Link zu site.icu-project.org
DevSolar
31

boost :: iequals ist im Fall von string nicht mit utf-8 kompatibel. Sie können boost :: locale verwenden .

comparator<char,collator_base::secondary> cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;
  • Primär - Akzente und Groß- / Kleinschreibung ignorieren und nur Basisbuchstaben vergleichen. Zum Beispiel sind "Fassade" und "Fassade" gleich.
  • Sekundär - Groß- und Kleinschreibung ignorieren, aber Akzente berücksichtigen. "Fassade" und "Fassade" sind unterschiedlich, aber "Fassade" und "Fassade" sind gleich.
  • Tertiär - berücksichtigen Sie sowohl Fall als auch Akzente: "Fassade" und "Fassade" sind unterschiedlich. Interpunktion ignorieren.
  • Quartär - Berücksichtigen Sie alle Groß- und Kleinschreibung, Akzente und Interpunktion. Die Wörter müssen hinsichtlich der Unicode-Darstellung identisch sein.
  • Identisch - wie quaternär, aber vergleichen Sie auch Codepunkte.
Igor Miljakow
quelle
30

Mein erster Gedanke für eine Nicht-Unicode-Version war, so etwas zu tun:


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
quelle
20

Sie können strcasecmpunter Unix oder stricmpWindows verwenden.

Eine Sache, die bisher nicht erwähnt wurde, ist, dass es nützlich ist, zuerst die Länge der beiden Zeichenfolgen zu vergleichen, wenn Sie stl-Zeichenfolgen mit diesen Methoden verwenden, da diese Informationen Ihnen bereits in der Zeichenfolgenklasse zur Verfügung stehen. Dies könnte verhindern, dass der kostspielige Zeichenfolgenvergleich durchgeführt wird, wenn die beiden Zeichenfolgen, die Sie vergleichen, überhaupt nicht gleich lang sind.

Bradtgmurray
quelle
Da das Bestimmen der Länge einer Zeichenfolge darin besteht, jedes Zeichen in der Zeichenfolge zu durchlaufen und mit 0 zu vergleichen, gibt es wirklich einen so großen Unterschied zwischen dem und dem sofortigen Vergleichen der Zeichenfolgen? Ich denke, Sie erhalten eine bessere Speicherlokalität, wenn beide Zeichenfolgen nicht übereinstimmen, aber im Falle einer Übereinstimmung wahrscheinlich fast die doppelte Laufzeit.
uliwitness
3
C ++ 11 gibt an, dass die Komplexität von std :: string :: length konstant sein muss: cplusplus.com/reference/string/string/length
bradtgmurray
1
Das ist eine lustige kleine Tatsache, hat aber hier wenig Einfluss. strcasecmp () und stricmp () verwenden beide nicht dekorierte C-Strings, sodass kein std :: string beteiligt ist.
uliwitness
3
Diese Methoden geben -1 zurück, wenn Sie "a" mit "ab" vergleichen. Die Längen sind unterschiedlich, aber "a" steht vor "ab". Ein einfacher Vergleich der Längen ist also nicht möglich, wenn der Anrufer sich um die Bestellung kümmert.
Nathan
14

Visual C ++ - Zeichenfolgenfunktionen, die Unicode unterstützen: http://msdn.microsoft.com/en-us/library/cc194799.aspx

die, die Sie wahrscheinlich suchen, ist _wcsnicmp

Darren Kopp
quelle
7
Ironischerweise sind die "Wide Character Codes" von Microsoft NICHT Unicode-sauber, da sie keine Unicode-Normalisierung verarbeiten.
vy32
13

Ich versuche, eine gute Antwort aus allen Beiträgen zusammenzustellen, also hilf mir, diese zu bearbeiten:

Hier ist eine Methode, um dies zu tun, obwohl sie die Zeichenfolgen transformiert und nicht Unicode-freundlich ist, sollte sie portabel sein, was ein Plus ist:

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

Nach dem, was ich gelesen habe, ist dies portabler als stricmp (), da stricmp () nicht Teil der Standardbibliothek ist, sondern nur von den meisten Compiler-Anbietern implementiert wird.

Um eine wirklich Unicode-freundliche Implementierung zu erhalten, müssen Sie anscheinend die Standardbibliothek verlassen. Eine gute Bibliothek von Drittanbietern ist die IBM ICU (International Components for Unicode).

Auch boost :: iequals bietet ein ziemlich gutes Dienstprogramm für diese Art von Vergleich.

Adam
quelle
Kannst du bitte sagen, was bedeutet :: tolower, warum du tolower anstelle von tolower () verwenden kannst und was ist '::' vorher? danke
VextoR
17
Dies ist keine sehr effiziente Lösung - Sie erstellen Kopien beider Zeichenfolgen und transformieren sie alle, auch wenn das erste Zeichen unterschiedlich ist.
Timmmm
2
Wenn Sie trotzdem eine Kopie erstellen möchten, warum nicht als Wert anstatt als Referenz übergeben?
Celticminstrel
Ich denke, es ist ein einfacher Tipp ohne Boost. :)
cmcromance
1
Die Frage fragt ausdrücklich, nicht transformdie gesamte Zeichenfolge vor dem Vergleich
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);})

Sie können den obigen Code in C ++ 14 verwenden, wenn Sie nicht in der Lage sind, Boost zu verwenden. Sie müssen std::towlowerfür breite Zeichen verwenden.

Weinstock
quelle
4
Ich denke, Sie müssen str1.size() == str2.size() &&der Vorderseite ein hinzufügen , damit die Grenzen nicht überschritten werden, wenn str2 ein Präfix von str1 ist.
ɲeuroburɳ
11

Der Boost.String Bibliothek verfügt über zahlreiche Algorithmen für Vergleiche ohne Berücksichtigung der Groß- und Kleinschreibung usw.

Sie könnten Ihre eigenen implementieren, aber warum sollten Sie sich die Mühe machen, wenn dies bereits geschehen ist?

Dean Harding
quelle
1
Es gibt keinen Weg, der mit std :: string integriert ist?
WilliamKF
6
Nein, gibt es nicht.
Dean Harding
3
"... warum sich die Mühe machen, wenn es schon fertig ist?" - Was ist, wenn Sie Boost nicht verwenden? Das OP hatte das Tag mit der Frage nicht.
JWW
11

Zu Ihrer Information, strcmp()und stricmp()sind anfällig für Pufferüberlauf, da sie nur verarbeitet werden, bis sie einen Null-Terminator erreichen. Es ist sicherer zu bedienen _strncmp()und _strnicmp().

Keil
quelle
6
Richtig, obwohl das Überlesen eines Puffers wesentlich weniger gefährlich ist als das Überschreiben eines Puffers.
Adam Rosenfield
4
stricmp()und strnicmp()sind nicht Teil des POSIX-Standards :-( Wie auch immer Sie finden strcasecmp()können strcasecmp_l(), strncasecmp()und strncasecmp_l()im POSIX-Header strings.h:-) siehe opengroup.org
olibre
2
@AdamRosenfield 'schlimmer' hängt vom Kontext ab. In der Sicherheit besteht der springende Punkt beim Überschreiben manchmal darin, überlesen zu werden.
Karmakaze
10

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

Demo

Brian Rodriguez
quelle
Diese Methode ist möglicherweise unsicher und nicht portabel. std::tolowerfunktioniert nur, wenn das Zeichen ASCII-codiert ist. Es gibt keine solche Garantie für std::string- so kann es leicht undefiniertes Verhalten sein.
Plasmacel
@plasmacel Verwenden Sie dann eine Funktion, die mit anderen Codierungen funktioniert.
Brian Rodriguez
9

Für meine grundlegenden Anforderungen zum Vergleichen von Zeichenfolgen, bei denen die Groß- und Kleinschreibung nicht berücksichtigt wird, möchte ich keine externe Bibliothek verwenden, und ich möchte auch keine separate Zeichenfolgenklasse mit Merkmalen, bei denen die Groß- und Kleinschreibung nicht berücksichtigt wird und die nicht mit allen anderen Zeichenfolgen kompatibel ist.

Was ich mir also ausgedacht habe, ist Folgendes:

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

Eine einfache Funktion mit einer Überladung für char und einer anderen für whar_t. Verwendet nichts, was nicht dem Standard entspricht, sollte also auf jeder Plattform in Ordnung sein.

Der Gleichheitsvergleich berücksichtigt keine Probleme wie Codierung mit variabler Länge und Unicode-Normalisierung, aber basic_string unterstützt das sowieso nicht und ist normalerweise kein Problem.

In Fällen, in denen eine ausgefeiltere lexikografische Manipulation von Text erforderlich ist, müssen Sie einfach eine Drittanbieter-Bibliothek wie Boost verwenden, was zu erwarten ist.

Neutrino
quelle
2
Sie könnten diese eine Funktion wahrscheinlich machen, wenn Sie sie zu einer Vorlage machen und basic_string <T> anstelle separater String- / Wstring-Versionen verwenden würden?
uliwitness
2
Wie würde die einzelne Funktionsvorlage entweder toupper oder towupper aufrufen, ohne auf Spezialisierung oder Makros zurückzugreifen? Eine Funktionsüberladung scheint eine einfachere und angemessenere Implementierung zu sein als beide.
Neutrino
9

Kurz und nett. Keine anderen Abhängigkeiten als erweiterte std C lib.

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

gibt true zurück , wenn str1und str2gleich sind. strcasecmpkann nicht existieren, es könnte Analoga geben stricmp,strcmpi usw.

Beispielcode:

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

Ausgabe:

true
true
true
true
true
kyb
quelle
6
Es ist seltsam, dass C ++ std :: string keine Vergleichsmethode für
Groß- und
1
"strcasecmp ist nicht Teil des Standards" - Mark Ransom 1. Dezember 14 um 19:57
Liviu
Ja, aber die meisten modernen Compiler haben es oder sein anderes Analogon. stricmp, strcmpi, strcasecmp, Danke usw.. Nachricht bearbeitet.
Kyb
TODO: Verwenden Sie cout << boolalphaanstelle von my, bool2strweil es Bool implizit in Zeichen für Stream konvertiert.
Kyb
Es befindet sich in <strings.h> in den Bibliotheken von gcc.
Eule
7

Dies ohne Boost zu tun, kann erreicht werden, indem der C-String-Zeiger mit c_str()und verwendet wird strcasecmp:

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

Angenommen, Sie suchen nach einer Methode und nicht nach einer bereits existierenden magischen Funktion, gibt es offen gesagt keinen besseren Weg. Wir könnten alle Code-Schnipsel mit cleveren Tricks für begrenzte Zeichensätze schreiben, aber am Ende des Tages müssen Sie die Zeichen irgendwann konvertieren.

Der beste Ansatz für diese Konvertierung besteht darin, dies vor dem Vergleich zu tun. Dies ermöglicht Ihnen ein hohes Maß an Flexibilität bei Codierungsschemata, die Ihr tatsächlicher Vergleichsoperator nicht kennen sollte.

Sie können diese Konvertierung natürlich hinter Ihrer eigenen Zeichenfolgenfunktion oder -klasse 'verbergen', aber Sie müssen die Zeichenfolgen vor dem Vergleich noch konvertieren.

Andrew Grant
quelle
6

Ich habe eine Version von char_traits ohne Berücksichtigung der Groß- und Kleinschreibung für die Verwendung mit std :: basic_string geschrieben, um einen std :: string zu generieren, bei dem bei Vergleichen, Suchen usw. mit den integrierten std :: basic_string-Elementfunktionen nicht zwischen Groß- und Kleinschreibung unterschieden wird.

Mit anderen Worten, ich wollte so etwas machen.

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

assert( a == b );

... was std :: string nicht verarbeiten kann. Hier ist die Verwendung meiner neuen char_traits:

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

assert( a == b );

... und hier ist die Implementierung:

/*  ---

        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
quelle
1
Dies funktioniert für normale Zeichen, aber nicht für alle Unicode-Zeichen, da die Captitalisierung nicht unbedingt bidirektional ist (es gibt ein gutes Beispiel auf Griechisch, das Sigma beinhaltet, an das ich mich derzeit nicht erinnern kann; so etwas hat zwei Klein- und einen Großbuchstaben , und Sie können keinen richtigen Vergleich so oder so bekommen)
coppro
1
Das ist wirklich der falsche Weg. Die Groß- und Kleinschreibung sollte keine Eigenschaft der Zeichenfolgen selbst sein. Was passiert, wenn dasselbe Zeichenfolgenobjekt Vergleiche zwischen Groß- und Kleinschreibung und Groß- und Kleinschreibung benötigt?
Ferruccio
Wenn die Groß- und Kleinschreibung nicht geeignet ist, "Teil" der Zeichenfolge zu sein, ist die Funktion find () überhaupt nicht vorhanden. Was für Sie wahr sein könnte, und das ist in Ordnung. IMO ist das Beste an C ++, dass es dem Programmierer kein bestimmtes Paradigma aufzwingt. Es ist das, was du willst / brauchst.
John Dibling
Eigentlich denke ich, dass die meisten C ++ - Guru (wie die im Standardkomitee) der Meinung sind, dass es ein Fehler war, find () in std :: basic_string <> zusammen mit vielen anderen Dingen zu setzen, die ebenso gut platziert werden könnten freie Funktionen. Außerdem gibt es einige Probleme beim Einfügen in den Typ.
Andreas Magnusson
Wie andere bereits betont haben, gibt es zwei Hauptprobleme bei dieser Lösung (ironischerweise ist eines die Schnittstelle und das andere die Implementierung ;-)).
Konrad Rudolph
4

Ich habe gute Erfahrungen mit der Verwendung der International Components for Unicode-Bibliotheken gemacht - sie sind äußerst leistungsfähig und bieten Methoden für die Konvertierung, die Unterstützung von Gebietsschemas, das Rendern von Datum und Uhrzeit, die Fallzuordnung (die Sie anscheinend nicht möchten) und die Sortierung . Dies beinhaltet einen Vergleich zwischen Groß- und Kleinschreibung und Akzent (und mehr). Ich habe nur die C ++ - Version der Bibliotheken verwendet, aber sie scheinen auch eine Java-Version zu haben.

Es gibt Methoden, um normalisierte Vergleiche durchzuführen, auf die von @Coincoin verwiesen wird, und sie können sogar das Gebietsschema berücksichtigen - zum Beispiel (und dies ist ein Sortierbeispiel, das nicht streng gleich ist), traditionell auf Spanisch (in Spanien), zwischen denen die Buchstabenkombination "ll" sortiert "l" und "m", also "lz" <"ll" <"ma".

Blair Conrad
quelle
4

Verwenden Sie diese Option nur zum Vergleich zwischen strcmp()Groß- und Kleinschreibung und / strcmpi()oder stricmp()zum Vergleich zwischen Groß- und Kleinschreibung. Welche sind beide in der Header-Datei<string.h>

Format:

int strcmp(const char*,const char*);    //for case sensitive
int strcmpi(const char*,const char*);   //for case insensitive

Verwendungszweck:

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;

Ausgabe

Apfel und ApPlE sind gleich

a kommt vor b, also kommt Apfel vor Ball

Rubenjohn
quelle
2
Downvote, weil dies kaum eine C ++ - Methode ist.
Thomas Daugaard
Dies ist die C ++ - Konvention an meiner Universität, aber ich werde sie beim Posten hier berücksichtigen
Rubenjohn
4
stricmp ist eine Microsoft-Erweiterung AFAIK. BSD scheint stattdessen strcasecmp () zu haben.
Zeuge
3

Spät zur Party, aber hier ist eine Variante, die std::localeTürkisch verwendet und damit richtig umgeht:

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

gibt Ihnen einen Funktor, der das aktive Gebietsschema verwendet, um Zeichen in Kleinbuchstaben umzuwandeln, über die Sie dann Zeichenfolgen in Kleinbuchstaben std::transformgenerieren können:

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

Dies funktioniert auch für wchar_tbasierte Zeichenfolgen.

Simon Richter
quelle
2

Nur ein Hinweis zu der Methode, die Sie letztendlich auswählen, falls diese Methode die Verwendung von beinhaltet strcmp , schlagen einige Antworten vor:

strcmpfunktioniert im Allgemeinen nicht mit Unicode-Daten. Im Allgemeinen funktioniert es nicht einmal mit bytebasierten Unicode-Codierungen wie utf-8, da strcmpnur Byte-pro-Byte-Vergleiche durchgeführt werden und in utf-8 codierte Unicode-Codepunkte mehr als 1 Byte benötigen können. Der einzige spezifische Unicode-Fall, der strcmprichtig behandelt wird, ist, wenn eine mit einer bytebasierten Codierung codierte Zeichenfolge nur Codepunkte unter U + 00FF enthält - dann ist der Vergleich von Byte pro Byte ausreichend.

Johann Gerell
quelle
2

Ab Anfang 2013 ist das von IBM gepflegte ICU-Projekt eine ziemlich gute Antwort darauf.

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

ICU ist eine "vollständige, tragbare Unicode-Bibliothek, die die Industriestandards genau verfolgt". Für das spezifische Problem des Zeichenfolgenvergleichs macht das Kollatierungsobjekt das, was Sie wollen.

Das Mozilla-Projekt hat Mitte 2012 die Intensivstation für die Internationalisierung in Firefox verabschiedet. Hier können Sie die technische Diskussion verfolgen, einschließlich Fragen zu Buildsystemen und Datendateigröße:

Michaelhanson
quelle
2

Es sieht so aus, als ob die oben genannten Lösungen nicht die Vergleichsmethode verwenden und total erneut implementieren. Hier ist meine Lösung und ich hoffe, sie funktioniert für Sie (sie funktioniert einwandfrei).

#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
quelle
1

Wenn Sie die Boost-Bibliothek nicht verwenden möchten, finden Sie hier eine Lösung, bei der nur der C ++ - Standard-Io-Header verwendet wird.

#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
quelle
Ich glaube, std :: toupper befindet sich in #include <cctype>. Möglicherweise müssen Sie es einschließen.
David Ledger
Wenn Sie eine globale Version wie diese :: toupper verwenden, müssen Sie möglicherweise <ctype> nicht einschließen, da es vermutlich zwei Versionen c-Version und c ++ - Version mit Gebietsschema gibt. Verwenden Sie also besser die globale Version ":: toupper ()"
HaSeeB MiR
Diese Lösung schlägt fehl, wenn eine der Zeichenfolgen leer ist: "" - gibt in diesem Fall true zurück, wenn false zurückgegeben werden soll
ekkis
0

Wenn Sie eine Quellzeichenfolge häufiger mit anderen Zeichenfolgen vergleichen müssen, besteht eine elegante Lösung darin, Regex zu verwenden.

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
quelle
Versuchte dies, aber kompilierte Fehler: error: conversion from 'const char [5]' to non-scalar type 'std::wstring {aka std::basic_string<wchar_t>}' requested
Deqing
schlechte Idee. Es ist die schlechteste Lösung.
Behrouz.M
Dies ist keine gute Lösung, aber selbst wenn Sie sie verwenden möchten, benötigen Sie ein L vor Ihren breitesten Konstanten, z. B. L "TEST"
Celticminstrel
Wäre schön, wenn jemand erklären könnte, warum es die schlechteste Lösung ist. Wegen Leistungsproblemen? Das Erstellen des regulären Ausdrucks ist teuer, aber danach sollte der Vergleich sehr schnell sein.
Smibe
Es ist verwendbar und portabel. Das Hauptproblem besteht darin, dass es zunächst keine Zeichen enthalten kann, die Regex verwendet. Aus diesem Grund kann es nicht als allgemeiner Zeichenfolgenvergleich verwendet werden. Es wird auch langsamer sein, es gibt eine Flagge, damit es so funktioniert, wie es smibe sagt, aber es kann immer noch nicht als allgemeine Funktion verwendet werden.
Ben
0

Eine einfache Möglichkeit, zwei Zeichenfolgen in c ++ (für Windows getestet) zu vergleichen, ist die Verwendung von _stricmp

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

Wenn Sie mit std :: string verwenden möchten, ein Beispiel:

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

Weitere Informationen finden Sie hier: https://msdn.microsoft.com/it-it/library/e0z9k731.aspx

DAme
quelle
Zusätzlich zu dieser Antwort lohnt es sich, stackoverflow.com/a/12414441/95309 zu lesen , da es a) eine C-Funktion und b) angeblich nicht portabel ist.
Claus Jørgensen
Was #include brauchen wir, damit das funktioniert?
Ekkis
1
@ekkis um _stricmp zu verwenden, müssen Sie <string.h> einschließen, wie Sie hier lesen können: 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;
        }
      }
}

Dies könnte wahrscheinlich viel effizienter gemacht werden, aber hier ist eine sperrige Version mit all ihren Teilen.

nicht allzu tragbar, funktioniert aber gut mit allem, was sich auf meinem Computer befindet (keine Ahnung, ich bin von Bildern, nicht von Worten)

user4578093
quelle
Dies ist keine Unicode-Unterstützung, wie in der Frage gestellt.
Behrouz.M
Dies unterstützt keine nicht englischen Zeichensätze.
Robert Andrzejuk
-3

Eine einfache Möglichkeit, Zeichenfolgen zu vergleichen, die sich nur durch Kleinbuchstaben und Großbuchstaben unterscheiden, ist ein ASCII-Vergleich. Alle Groß- und Kleinbuchstaben unterscheiden sich in der ASCII-Tabelle um 32 Bit. Mit diesen Informationen haben wir Folgendes ...

    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
quelle
3
Demnach wird "++ j" gleich "KKJ" und "1234" gleich "QRST" gefunden. Ich bezweifle, dass das jemand will.
Celticminstrel