C ++ dilinde büyük / küçük harfe duyarlı olmayan dize karşılaştırması [kapalı]


373

Bir dizgiyi büyük harfe veya küçük harfe dönüştürmeden C ++ ile büyük / küçük harfe duyarlı olmayan karşılaştırma yapmanın en iyi yolu nedir?

Lütfen yöntemlerin Unicode dostu olup olmadığını ve ne kadar taşınabilir olduklarını belirtin.


@ [Adam] (# 11679): Bu değişken kullanılabilirlik açısından iyi olsa da, gereksiz kopyalar yarattığı için performans açısından kötü. Bir şey göz ardı edebilir ama en iyi (Unicode olmayan) yol kullanmak olduğuna inanıyorum std::stricmp. Aksi takdirde Herb'in söylediklerini okuyun .
Konrad Rudolph

C olarak, bir kişi genellikle tüm dizeyi değiştirmek zorunda kaldı ve bu şekilde karşılaştırın - ya da kendi karşılaştırmanızı yuvarlayın: P
Michael Dorgan

daha sonraki bir sorunun daha basit bir cevabı vardır: strcasecmp (en azından BSD ve POSIX derleyicileri için) stackoverflow.com/questions/9182912/…
Móż

@ Mσᶎ da bu sorunun cevabı, strcasecmpstandardın bir parçası olmayan ve en az bir ortak derleyiciden eksik olan önemli uyarı ile birlikte .
Mark Ransom

Yanıtlar:


317

Boost bunun için kullanışlı bir algoritma içerir:

#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
Bu UTF-8 dostu mu? Bence değil.
vladr

18
Hayır, UTF-8 aksanlar, biçerdöverler, bidi sorunları
vb.Nedeniyle

10
@ vy32 Bu kesinlikle yanlış! UTF-8 kombinasyonları birbirini dışlar. Her zaman mümkün olan en kısa temsili kullanmalıdır, eğer değilse, dikkatle ele alınması gereken hatalı biçimlendirilmiş bir UTF-8 dizisi veya kod noktasıdır.
Wiz

48
@Wiz, Unicode dize normalizasyonu konusunu görmezden geliyorsunuz. ñ birleştirme ˜ ve ardından n veya ñ karakteri ile temsil edilebilir. Karşılaştırmayı gerçekleştirmeden önce Unicode dize normalleştirmesi kullanmanız gerekir. Unicode Teknik Rapor # 15 inceleyin unicode.org/reports/tr15
vy32

12
@wonkorealtime: büyük harfe dönüştürülür "ß" olduğu için "SS": fileformat.info/info/unicode/char/df/index.htm
Ördek mölemeye

118

Standarttan yararlanın char_traits. A'nın std::stringaslında bir typedef olduğunu std::basic_string<char>veya daha açık olduğunu hatırlayın std::basic_string<char, std::char_traits<char> >. char_traitsTipi üzerine yeni bir dize typedef karakterler onlar vb All döküm nasıl, bunlar kopya nasıl karşılaştırmak yapmanız gereken açıklar basic_stringve kendi özel ile bunu sağlamak char_traitsduyarsızca davayı karşılaştırın.

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;

Detaylar 29. haftanın gurusu .


10
Bildiğim kadarıyla kendi deneylerime göre, bu yeni dize türünü std :: string ile uyumsuz hale getirir.
Zan Lynx

8
Tabii ki öyle - kendi iyiliği için. Büyük / küçük harfe duyarlı olmayan bir dize başka bir şeydir: typedef std::basic_string<char, ci_char_traits<char> > istringdeğil typedef std::basic_string<char, std::char_traits<char> > string.
Andreas Spindler

232
"Tek yapmanız gereken ..."
Tim MB

3
@Nathan muhtemelen kodda temel ÖAM gerçekleştirebilen bir derleyici kullanır ...
Paramanyetik Kruvasan

17
Bu önemsiz durumda böyle deliliği zorlayan herhangi bir dil yapısı pişmanlık duymadan terk edilmeli ve terk edilmelidir.
Erik Aronesty

86

Güçlendirme ile ilgili sorun, güçlendirme ile bağlantı kurmanız ve ona bağlı olmanızdır. Bazı durumlarda kolay değildir (örneğin android).

Ve char_traits kullanmak, tüm karşılaştırmalarınızın büyük / küçük harfe duyarlı olmadığı anlamına gelir ; bu genellikle istediğiniz şey değildir.

Bu yeterli olmalı. Oldukça verimli olmalıdır. Unicode veya başka bir şeyle uğraşmaz.

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

Güncelleme: Bonus C ++ 14 sürümü ( #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
Aslında, boost dizesi kütüphanesi sadece başlık sadece bir kütüphanedir, bu yüzden herhangi bir şeye bağlamaya gerek yoktur. Ayrıca, yalnızca dize başlıklarını kaynak ağacınıza kopyalamak için boost'un 'bcp' yardımcı programını kullanabilirsiniz, böylece tam boost kitaplığına ihtiyacınız yoktur.
Gretchen

Ah bcp hakkında bir şey bilmiyordum, gerçekten faydalı görünüyor. Bilgi için teşekkürler!
Timmmm

9
Basit ve artırmaya bağımlı olmayan bir sürümü bilmek güzel.
Deqing

2
@Anna Metin takviyesi kütüphanesi oluşturulmalı ve bağlanmalıdır. IBM ICU kullanır.
Behrouz.M

Ayrıca C ++ 11
martian

58

POSIX sistemindeyseniz strcasecmp kullanabilirsiniz . Bu işlev standart C'nin bir parçası değildir, ancak Windows'da da yoktur. Bu, yerel ayar POSIX olduğu sürece 8 bitlik grafiklerde büyük / küçük harfe duyarlı olmayan bir karşılaştırma yapar. Yerel ayar POSIX değilse, sonuçlar tanımsızdır (bu nedenle yerelleştirilmiş bir karşılaştırma yapabilir veya olmayabilir). Geniş karakter eşdeğeri yok.

Bunu yapmazsanız, çok sayıda tarihi C kütüphanesi uygulaması stricmp () ve strnicmp () işlevlerine sahiptir. Windows'taki Visual C ++, ANSI standardının bir parçası olmadıkları için bunları bir alt çizgi ile önek olarak yeniden adlandırdı, bu nedenle bu sisteme _stricmp veya _strnicmp denir . Bazı kütüphaneler ayrıca geniş karakterli veya çok baytlı eşdeğer işlevlere sahip olabilir (genellikle wcsicmp, mbcsicmp vb. Olarak adlandırılır).

C ve C ++ her ikisi de büyük ölçüde uluslararasılaşma sorunlarından habersizdir, bu nedenle üçüncü taraf kitaplığı kullanmak dışında bu soruna iyi bir çözüm yoktur. C / C ++ için sağlam bir kütüphaneye ihtiyacınız varsa IBM ICU'ya (Unicode için Uluslararası Bileşenler) göz atın . YBÜ hem Windows hem de Unix sistemleri içindir.


53

Aptal bir durum duyarsız karşılaştırma veya tam normalleştirilmiş bir Unicode karşılaştırmasından mı bahsediyorsunuz?

Aptal bir karşılaştırma, aynı olabilecek ancak ikili eşit olmayan dizeler bulamaz.

Misal:

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

Hepsi eşdeğerdir ancak farklı ikili gösterimlere de sahiptirler.

Bununla birlikte, özellikle Hangul, Thaï ve diğer Asya dillerini desteklemeyi planlıyorsanız , Unicode Normalizasyonu zorunlu bir okuma olmalıdır.

Ayrıca, IBM hemen hemen en iyileştirilmiş Unicode algoritmalarını patentledi ve herkese açık hale getirdi. Ayrıca bir uygulama da sürdürüyorlar: IBM ICU


2
Bu YBÜ bağlantısını site.icu-project.org
DevSolar

31

boost :: iequals, string durumunda utf-8 uyumlu değildir. Boost :: locale komutunu kullanabilirsiniz .

comparator<char,collator_base::secondary> cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;
  • Birincil - yalnızca temel harfleri karşılaştırarak aksanları ve karakter büyüklüğünü yok sayın. Örneğin "cephe" ve "Cephe" aynıdır.
  • İkincil - karakter büyüklüğünü yok sayın, ancak aksanları dikkate alın. "Cephe" ve "Cephe" farklıdır, ancak "Cephe" ve "Cephe" aynıdır.
  • Üçüncül - hem durumu hem de aksanları düşünün: "Cephe" ve "cephe" farklıdır. Noktalama işaretlerini yoksay.
  • Kuaterner - tüm durum, aksan ve noktalama işaretlerini göz önünde bulundurun. Sözcükler, Unicode temsili açısından aynı olmalıdır.
  • Aynı - kuaterner olarak, ancak kod noktalarını da karşılaştırın.

30

Unicode olmayan bir versiyon için ilk düşüncem böyle bir şey yapmaktı:


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

strcasecmpUnix veya stricmpWindows üzerinde kullanabilirsiniz .

Şimdiye kadar bahsedilmeyen bir şey, bu yöntemlerle stl dizeleri kullanıyorsanız, ilk önce iki dizenin uzunluğunu karşılaştırmak yararlı olacaktır, çünkü bu bilgi dize sınıfında zaten mevcuttur. Bu, karşılaştırdığınız iki dize ilk etapta aynı uzunlukta değilse, pahalı dize karşılaştırmasının yapılmasını engelleyebilir.


Bir dizginin uzunluğunu belirlemek, dizgideki her karakterin yinelemesini ve 0 ile karşılaştırılmasını içerdiğinden, gerçekten çok fazla fark var mı ve sadece dizeleri hemen karşılaştırmak mı? Her iki dizginin eşleşmediği durumda daha iyi bir bellek konumu elde edersiniz, ancak bir eşleşme durumunda muhtemelen yaklaşık 2x çalışma zamanı.
uliwitness

3
C ++ 11, std :: string :: uzunluğunun karmaşıklığının sabit olması gerektiğini belirtir
bradtgmurray

1
Bu eğlenceli küçük bir gerçek, ama burada çok az etkisi var. strcasecmp () ve stricmp () her ikisi de süslenmemiş C dizeleri alır, bu nedenle std :: dizesi yoktur.
uliwitness

3
"A" ve "ab" ile karşılaştırırsanız bu yöntemler -1 değerini döndürür. Uzunluklar farklı ama "a", "ab" den önce geliyor. Bu nedenle, arayan sipariş vermeyi önemsiyorsa, uzunlukları karşılaştırmak mümkün değildir.
Nathan


13

Tüm yayınlardan iyi bir cevap birlikte toparlanmaya çalışıyorum, bu yüzden bunu düzenlememe yardım et:

İşte bunu yapmak için bir yöntem, dizeleri dönüştürmesine rağmen ve Unicode dostu olmasa da, bir artı olan taşınabilir olmalıdır:

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

Ne okudum itibaren bu stricmp () aslında std kütüphanesinin bir parçası değil, sadece çoğu derleyici satıcıları tarafından uygulanan çünkü stricmp () daha taşınabilir.

Gerçekten Unicode dostu bir uygulama elde etmek için std kütüphanesinin dışına gitmek gerekir görünüyor. İyi bir 3. taraf kütüphanesi IBM ICU'dur (Unicode Uluslararası Bileşenleri)

Ayrıca boost :: iequals bu tür bir karşılaştırma yapmak için oldukça iyi bir yardımcı program sağlar.


lütfen ne anlama geldiğini söyleyebilir misiniz :: tolower, neden tolower () yerine tolower'ı kullanabilirsiniz ve daha önce '::' nedir? teşekkürler
VextoR

17
Bu çok verimli bir çözüm değildir - ilk karakter farklı olsa bile her iki dizginin kopyalarını oluşturur ve hepsini dönüştürürsünüz.
Timmmm

2
Yine de bir kopya yapacaksanız neden referans yerine değere göre geçmiyorsunuz?
celticminstrel

Bence destek olmadan basit bir ipucu. :)
cmcromance

1
soru açıkça transformkarşılaştırmadan önce tüm dizeyi değil soruyor
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);})

Boost kullanmak için bir konumda değilseniz, yukarıdaki kodu C ++ 14'te kullanabilirsiniz. std::towlowerGeniş karakterler için kullanmanız gerekir .


4
Ben str1.size() == str2.size() &&str2 str1 bir önek olduğunda sınırların dışına çıkmayacak şekilde ön eklemek gerekir düşünüyorum .
ɳeuroburɳ

11

Boost.String kütüphane vb harf insenstive karşılaştırmalar yapıyor ve için algoritmaların bir yeri vardır.

Kendinizi uygulayabilirsiniz, ama zaten bittiğinde neden rahatsız oluyorsunuz?


1
Std :: string ile yerleşik bir yol yok mu?
WilliamKF

6
Hayır, yok.
Dean Harding

3
“... zaten bittiğinde neden rahatsız oluyorsun?” - Boost kullanmıyorsanız ne olacak? OP'nin sorusu olan etiketi yoktu.
jww

11

FYI, strcmp()ve stricmp()boş bir sonlandırıcıya çarpana kadar işledikleri için arabellek taşmasına karşı savunmasızdırlar. Kullanımı daha güvenlidir _strncmp()ve _strnicmp().


6
Doğru, bir arabellek aşırı okumak bir arabellek yazmaktan daha az tehlikelidir.
Adam Rosenfield

4
stricmp()ve strnicmp():-( Ancak bulabilirsiniz POSIX standardının parçası değildir strcasecmp(), strcasecmp_l(), strncasecmp()ve strncasecmp_l()POSIX başlığındaki strings.h:-) bkz opengroup.org
olibre

2
@AdamRosenfield 'daha kötü' bağlama bağlıdır. Güvenlikte, bazen üzerine yazma işleminin tüm amacı aşırıya kaçmaktır.
karmakaze

10

Bakınız 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;
}

gösteri


Bu yöntem potansiyel olarak güvenli değildir ve taşınabilir değildir. std::toloweryalnızca karakter ASCII kodluysa çalışır. Böyle bir garanti yoktur std::string- bu nedenle kolayca tanımlanamayan davranış olabilir.
plasmacel

@plasmacel Ardından diğer kodlamalar ile çalışan bir işlev kullanın.
Brian Rodriguez

9

Benim temel büyük / küçük harf duyarsız dize karşılaştırma ihtiyaçları için ne harici bir kütüphane kullanmak zorunda ne de, diğer tüm dizeleri ile uyumsuz büyük / küçük harf duyarsız özellikleri ile ayrı bir dize sınıfı istiyorum.

Yani ortaya çıkardığım şey şu:

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

Char için bir aşırı yük ve whar_t için başka bir yük ile basit bir işlev. Standart olmayan hiçbir şey kullanmaz, bu nedenle herhangi bir platformda iyi olmalıdır.

Eşitlik karşılaştırması, değişken uzunluk kodlaması ve Unicode normalizasyonu gibi sorunları dikkate almayacaktır, ancak basic_string'in zaten farkında olduğum için hiçbir desteği yoktur ve normalde bir sorun değildir.

Metnin daha karmaşık sözlükbilimsel manipülasyonunun gerekli olduğu durumlarda, Boost gibi beklenen bir üçüncü taraf kütüphanesi kullanmanız yeterlidir.


2
Bir şablon oluşturduysanız ve ayrı dize / wstring sürümleri yerine basic_string <T> kullandıysanız, muhtemelen bir işlevi yapabilirsiniz?
uliwitness

2
Tek işlev şablonu, uzmanlık veya makro kullanımına başvurmadan nasıl toupper veya towupper'ı çağırır? Bir işlev aşırı yükü, her ikisinden de daha basit ve daha uygun bir uygulama gibi görünür.
Neutrino

9

Kısa ve güzel. Genişletilmiş std C lib'den başka bağımlılık yoktur .

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

ve eşitse true değerini döndürür . olmayabilir, olabilir analogları , vbstr1str2strcasecmpstricmpstrcmpi

Örnek kod:

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

Çıktı:

true
true
true
true
true

6
C ++ std :: string'in görmezden-durum karşılaştırma yöntemi olmaması garip ..
kyb

1
"strcasecmp standardın bir parçası değil" - Mark Ransom 1 Aralık 14:
Liviu

evet, ancak modern derleyicilerin çoğunda veya başka bir analog var. stricmp, strcmpi, strcasecmpVb teşekkür ederiz. mesaj düzenlendi.
kyb

YAPILACAKLAR: bool'u akış için karakterlere dolaylı olarak dönüştürmek için cout << boolalphabenim yerine kullanmak bool2str.
kyb

Gcc'nin kütüphanelerinde <strings.h> dizininde.
Baykuş

7

Boost kullanmadan bunu yapmak, C string işaretçisini c_str()alıp kullanarak yapılabilir strcasecmp:

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

6

Zaten var olan sihirli bir işlevi değil, bir yöntemi aradığınızı varsayarsak, daha iyi bir yol yoktur. Hepimiz sınırlı karakter setleri için akıllı numaralarla kod parçacıkları yazabiliriz, ancak günün sonunda karakterleri dönüştürmeniz gerekir.

Bu dönüşüm için en iyi yaklaşım karşılaştırmadan önce yapmaktır. Bu, gerçek karşılaştırma operatörünüzün bilmemesi gereken kodlama şemaları konusunda size esneklik sağlar.

Elbette bu dönüşümü kendi dize işlevinizin veya sınıfınızın arkasına 'gizleyebilirsiniz', ancak karşılaştırmadan önce dizeleri dönüştürmeniz gerekir.


6

Ben yerleşik std :: basic_string üye işlevlerini kullanarak karşılaştırma, arama, vb yaparken büyük-küçük harf duyarlı olmayan bir std :: string üretmek için char_traits büyük küçük harf duyarsız bir sürümünü yazdım.

Başka bir deyişle, böyle bir şey yapmak istedim.

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

assert( a == b );

... hangi std :: string işleyemez. İşte yeni char_traits'imin kullanımı:

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

assert( a == b );

... ve işte uygulama:

/*  ---

        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;

1
Bu normal karakterlerde işe yarıyor, ancak kapitalizasyonun çift yönlü olması gerekmediğinden tüm Unicode için işe yaramayacak (şu anda hatırlayamadığım sigma içeren Yunanca'da iyi bir örnek var; bunun gibi iki alt ve bir büyük harf var , ve her iki şekilde de doğru bir karşılaştırma
yapamazsınız

1
Bu konuda yanlış bir yol var. Büyük / küçük harf duyarlılığı, dizelerin kendilerinin bir özelliği olmamalıdır. Aynı dize nesnesi hem büyük / küçük harfe duyarlı hem de büyük / küçük harfe duyarlı olmayan karşılaştırmalar gerektirdiğinde ne olur?
Ferruccio

Büyük / küçük harf duyarlılığı, dizenin "parçası" olmak için uygun değilse, hiçbiri find () işlevi değildir. Hangi, sizin için doğru olabilir ve bu iyi. IMO, C ++ ile ilgili en büyük şey, programcı üzerinde belirli bir paradigmayı zorlamamasıdır. İstediğiniz / olması gereken budur.
John Dibling

Aslında, çoğu C ++ - guru's (standartlar komitesindekiler gibi) eşit olarak yerleştirilebilecek diğer birçok şeyle birlikte std :: basic_string <> 'de find () koymak için bir hata olduğunu kabul ediyorum serbest fonksiyonlar. Ayrıca türüne koymak ile ilgili bazı sorunlar vardır.
Andreas Magnusson

Diğerlerinin de belirttiği gibi, bu çözümle ilgili iki önemli şey yanlıştır (ironik olarak, bir arayüz ve diğeri uygulama ;-)).
Konrad Rudolph

4

Unicode kütüphaneleri için Uluslararası Bileşenler kullanma konusunda iyi bir deneyim yaşadım - son derece güçlüler ve dönüştürme, yerel ayar desteği, tarih ve saat oluşturma, vaka eşleme (istemediğiniz gibi görünmüyor) ve harmanlama için yöntemler sağlıyorlar , büyük ve küçük harf duyarlı karşılaştırma (ve daha fazlası) içerir. Sadece kütüphanelerin C ++ sürümünü kullandım, ancak Java sürümüne de sahipler.

@Coincoin tarafından atıfta bulunulan normalleştirilmiş karşılaştırmaları gerçekleştirmek için yöntemler vardır ve hatta yerel ayarları da hesaba katabilir - örneğin (ve bu kesinlikle eşitlik değil, bir sıralama örneği), İspanyolca'da (İspanya'da), harf kombinasyonu "ll" arasında sıralar "l" ve "m", bu yüzden "lz" <"ll" <"ma".


4

Büyük / strcmp()küçük harfe duyarlı ve / strcmpi()veya büyük / küçük harfe stricmp()duyarlı olmayan karşılaştırma için kullanın. Her ikisi de başlık dosyasında<string.h>

biçim:

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

Kullanımı:

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;

Çıktı

elma ve ApPlE aynı

a b'den önce gelir, yani elma toptan önce gelir


2
Downvote çünkü bu şeyler yapmak için bir C ++ yolu değildir.
Thomas Daugaard

Bu üniversitemdeki c ++ konvansiyonudur, ancak burada yayınlarken aklımızda tutacağım
reubenjohn

4
stricmp bir Microsoft uzantısı AFAIK. BSD'nin bunun yerine strcasecmp () olduğu görülüyor.
uliwitness

3

Partiye geç, ama işte std::localeTürkçe'yi kullanan ve dolayısıyla doğru şekilde işleyen bir varyant :

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

karakterleri küçük harfe dönüştürmek için etkin yerel ayarı kullanan ve daha sonra küçük harfli std::transformdizeler oluşturmak için kullanabileceğiniz bir işlev sağlar :

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

Bu aynı zamanda wchar_ttemel dizeler için de geçerlidir .


2

Sonunda hangi yöntemi seçerseniz seçin, eğer bu yöntem strcmpbazı cevapların kullanımını içeriyorsa :

strcmpgenel olarak Unicode verileriyle çalışmaz. Genel olarak, utf-8 gibi bayt tabanlı Unicode kodlamaları ile bile çalışmaz, çünkü strcmpsadece bayt başına bayt karşılaştırmaları yapar ve utf-8'de kodlanan Unicode kod noktaları 1 bayttan fazla sürebilir. Tek bir Unicode büyük / küçük harf strcmpkullanımı, bayt tabanlı kodlama ile kodlanan bir dizenin yalnızca U + 00FF altında kod noktaları içerdiği durumdur - o zaman bayt başına bayt karşılaştırması yeterlidir.


2

2013'ün başlarından itibaren, IBM tarafından sürdürülen YBÜ projesi buna oldukça iyi bir cevaptır.

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

ICU "endüstri standartlarını yakından takip eden eksiksiz, taşınabilir bir Unicode kütüphanesidir." Dize karşılaştırmasıyla ilgili özel sorun için, Collation nesnesi istediğinizi yapar.

Mozilla Projesi, 2012 ortasında Firefox'ta uluslararasılaşma için YBÜ'yi kabul etti; yapı sistemlerinin sorunları ve veri dosyası boyutu da dahil olmak üzere mühendislik tartışmasını buradan izleyebilirsiniz:


2

Yukarıdaki çözümler karşılaştırma yöntemi kullanmıyor ve toplamı tekrar uygulamıyor gibi görünüyor, işte benim çözümüm ve sizin için çalıştığını umuyoruz (iyi çalışıyor).

#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

Boost kütüphanesini kullanmak istemiyorsanız, burada sadece C ++ standart io üstbilgisini kullanarak çözüm bulabilirsiniz.

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

Sanırım std :: toupper #include <cctype> 'de, eklemeniz gerekebilir.
David Ledger

Bunun gibi global sürümü kullanacaksanız, toupper o zaman <ctype> eklemeniz gerekmeyebilir çünkü sanırım iki sürüm c sürümü ve c ++ sürümü var. Küresel sürümü kullanmak daha iyi "::
toupper

dizelerden biri boş olduğunda bu çözüm başarısız olur: "" - bu durumda false döndürmesi gerektiğinde true
değerini

0

Bir kaynak dizgiyi diğer dizelerle daha sık karşılaştırmanız gerekiyorsa, zarif bir çözüm regex kullanmaktır.

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

std::wregex pattern(first, std::wregex::icase);
bool isEqual = std::regex_match(second, pattern);

Bu ancak derleme hatası çalıştı: error: conversion from 'const char [5]' to non-scalar type 'std::wstring {aka std::basic_string<wchar_t>}' requested
Deqing

kötü bir fikir. En kötü çözüm.
Behrouz.M

Bu iyi bir çözüm değildir, ancak kullanmak
isteseniz bile

Birisi neden en kötü çözüm olduğunu açıklayabilir eğer iyi olurdu. Performans sorunları nedeniyle mi? Normal ifadeyi oluşturmak pahalıdır, ancak daha sonra karşılaştırma gerçekten hızlı olmalıdır.
smibe

kullanılabilir ve taşınabilir, asıl sorun, regex'in kullandığı herhangi bir karakteri içerememesi. Bu nedenle genel dize karşılaştırması olarak kullanılamaz. Ayrıca daha yavaş olacak, kokunun söylediği şekilde çalışmasını sağlayacak bir bayrak var, ancak yine de genel bir işlev olarak kullanılamıyor.
Ben

0

C ++ (windows için test edilmiş) içinde iki dizeyi karşılaştırmanın basit bir yolu _stricmp kullanmaktır

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

Std :: string ile kullanmak istiyorsanız, bir örnek:

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

Daha fazla bilgi için: https://msdn.microsoft.com/it-it/library/e0z9k731.aspx


Bu cevaba ek olarak stackoverflow.com/a/12414441/95309 , a) bir C işlevi ve b) sözde taşınabilir olmadığı için okumaya değer .
Claus Jørgensen

Bu işi yapmak için hangi #include'a ihtiyacımız var?
ekkis

1
@ekkis _stricmp kullanmak için, burada okuyabileceğiniz gibi <string.h> yazmanız gerekir: 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;
        }
      }
}

bu muhtemelen çok daha verimli hale getirilebilir, ancak burada tüm bitleri çıplak olan hantal bir versiyon.

tüm bu taşınabilir değil, ama bilgisayarımdaki her şeyle iyi çalışıyor (hiçbir fikrim yok, resimler değil kelimelerim)


Bu, sorunun sorduğu Unicode desteği değil.
Behrouz.M

İngilizce olmayan karakter setlerini desteklemez.
Robert Andrzejuk

-3

Yalnızca küçük harf ve büyük harfle yazılmış karakterlerden farklı olan dizeleri karşılaştırmanın kolay bir yolu, ASCII karşılaştırması yapmaktır. Tüm büyük ve küçük harfler ascii tablosunda 32 bit farklıdır, bu bilgileri kullanarak aşağıdakilere sahibiz ...

    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
Buna göre "++ j", "KKJ" ye ve "1234" ise "QRST" ye eşit bulunacaktır. Bunun kimsenin istediği bir şey olduğundan şüpheliyim.
celticminstrel
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.