372 Stimmen

Groß-/Kleinschreibung unempfindlicher String-Vergleich in C++

Was ist der beste Weg, um case-insensitive String-Vergleich in C++ ohne Umwandlung einer Zeichenfolge in alle Groß- oder Kleinschreibung zu tun?

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

9voto

Neutrino Punkte 6928

Für meine grundlegenden Groß-/Kleinschreibung unempfindlichen String-Vergleich Bedürfnisse bevorzuge ich nicht zu haben, eine externe Bibliothek zu verwenden, noch will ich eine separate String-Klasse mit Groß-/Kleinschreibung unempfindlichen Eigenschaften, die mit allen meinen anderen Zeichenfolgen inkompatibel ist.

Ich habe mir also Folgendes überlegt:

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 weiteren für whar_t. Verwendet nichts, was nicht dem Standard entspricht, und sollte daher auf jeder Plattform gut funktionieren.

Der Gleichheitsvergleich berücksichtigt keine Probleme wie die Kodierung mit variabler Länge und die Unicode-Normalisierung, aber basic_string hat dafür keine Unterstützung, soweit mir bekannt ist, und normalerweise ist das auch kein Problem.

In Fällen, in denen eine anspruchsvollere lexikografische Manipulation von Text erforderlich ist, müssen Sie einfach eine Bibliothek eines Drittanbieters wie Boost verwenden, was auch zu erwarten ist.

9voto

DavidS Punkte 1897

Um dies zu tun, ohne Boost zu verwenden, kann man den C-String-Zeiger mit c_str() und mit strcasecmp :

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

6voto

John Dibling Punkte 96619

Ich habe eine case-insensitive Version von char_traits für die Verwendung mit std::basic_string geschrieben, um einen std::string zu erzeugen, bei dem die Groß- und Kleinschreibung nicht beachtet wird, wenn man Vergleiche, Suchen usw. mit den eingebauten std::basic_string-Memberfunktionen durchführt.

Mit anderen Worten: Ich wollte etwas in dieser Art machen.

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

assert( a == b );

...die 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 Umsetzung:

/*  ---

        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;

6voto

Simon Richter Punkte 27154

Ich bin zwar spät dran, aber hier ist eine Variante, bei der std::locale und geht somit korrekt mit Türkisch um:

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, die Sie dann über std::transform um Zeichenketten in Kleinbuchstaben zu erzeugen:

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

Dies gilt auch für wchar_t basierten Zeichenketten.

6voto

Andrew Grant Punkte 57342

Wenn man davon ausgeht, dass man nach einer Methode sucht und nicht nach einer magischen Funktion, die bereits existiert, gibt es ehrlich gesagt keinen besseren Weg. Wir könnten alle Codeschnipsel mit cleveren Tricks für begrenzte Zeichensätze schreiben, aber am Ende des Tages müssen Sie die Zeichen irgendwann konvertieren.

Am besten ist es, diese Umrechnung vor dem Vergleich vorzunehmen. Dies ermöglicht Ihnen ein hohes Maß an Flexibilität, wenn es um Kodierungsschemata geht, die Ihrem eigentlichen Vergleichsoperator nicht bekannt sein sollten.

Sie können diese Konvertierung natürlich hinter Ihrer eigenen Stringfunktion oder -klasse "verstecken", aber Sie müssen die Strings vor dem Vergleich trotzdem konvertieren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X