Perbandingan string case-insensitive dalam C ++ [ditutup]

373

Apa cara terbaik untuk melakukan perbandingan string case-insensitive dalam C ++ tanpa mengubah string menjadi semua huruf besar atau semua huruf kecil?

Harap tunjukkan apakah metode ini ramah Unicode dan seberapa portabelnya.

Adam
sumber
@ [Adam] (# 11679): Meskipun varian ini bagus dalam hal kegunaan, buruk dalam hal kinerja karena membuat salinan yang tidak perlu. Saya mungkin mengabaikan sesuatu tetapi saya percaya cara terbaik (non-Unicode) adalah menggunakan std::stricmp. Jika tidak, membaca apa Herb katakan .
Konrad Rudolph
Dalam c, seseorang biasanya dipaksa untuk mengungguli seluruh string kemudian membandingkan dengan cara itu - atau roll membandingkan Anda sendiri: P
Michael Dorgan
pertanyaan selanjutnya memiliki jawaban yang lebih sederhana: strcasecmp (setidaknya untuk kompiler BSD & POSIX) stackoverflow.com/questions/9182912/…
Móż
@ Mσᶎ pertanyaan ini juga memiliki jawaban itu, dengan peringatan penting yang strcasecmpbukan bagian dari standar dan hilang dari setidaknya satu kompiler umum.
Mark Ransom

Jawaban:

318

Boost menyertakan algoritme yang berguna untuk ini:

#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
}
rampok
sumber
14
Apakah ini ramah UTF-8? Saya pikir tidak.
vladr
18
Tidak, karena UTF-8 memungkinkan string identik dikodekan dengan kode biner yang berbeda, karena aksen, gabungan, masalah bidi, dll.
vy32
10
@ vy32 Itu benar-benar salah! Kombinasi UTF-8 saling eksklusif. Itu harus selalu menggunakan representasi sesingkat mungkin, jika tidak, itu urutan UTF-8 yang salah atau titik kode yang harus diperlakukan dengan hati-hati.
Wiz
48
@ Wiz, Anda mengabaikan masalah normalisasi string Unicode. ñ dapat direpresentasikan sebagai gabungan ˜ diikuti oleh n, atau dengan karakter ñ. Anda perlu menggunakan normalisasi string Unicode sebelum melakukan pembandingan. Harap tinjau Unicode Laporan Teknis # 15, unicode.org/reports/tr15
vy32
12
@wonkorealtime: karena "ß" yang dikonversi ke huruf besar adalah "SS": fileformat.info/info/unicode/char/df/index.htm
Mooing Duck
118

Manfaatkan standar ini char_traits. Ingatlah bahwa std::stringsebenarnya adalah typedef untuk std::basic_string<char>, atau lebih eksplisit std::basic_string<char, std::char_traits<char> >,. The char_traitsTipe menjelaskan bagaimana karakter membandingkan, bagaimana mereka menyalin, bagaimana mereka melemparkan dll Semua perlu Anda lakukan adalah typedef string baru lebih basic_string, dan memberikan dengan kustom Anda sendiri char_traitsyang membandingkan kasus insensitively.

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;

Rinciannya ada di Guru of The Week nomor 29 .

wilhelmtell
sumber
10
Sejauh yang saya tahu dari eksperimen saya sendiri, ini membuat tipe string baru Anda tidak kompatibel dengan std :: string.
Zan Lynx
8
Tentu saja - untuk kebaikannya sendiri. String case-insensitive adalah sesuatu yang lain:, typedef std::basic_string<char, ci_char_traits<char> > istringtidak typedef std::basic_string<char, std::char_traits<char> > string.
Andreas Spindler
232
"Yang perlu Anda lakukan ..."
Tim MB
3
@Nathan mungkin menggunakan kompiler yang mampu melakukan CSE dasar pada kode ...
The Paramagnetic Croissant
17
Setiap konstruksi bahasa yang memaksa kegilaan semacam itu dalam kasus sepele ini harus dan dapat ditinggalkan tanpa penyesalan.
Erik Aronesty
86

Masalahnya dengan peningkatan adalah Anda harus terhubung dengan dan bergantung pada dorongan. Tidak mudah dalam beberapa kasus (mis. Android).

Dan menggunakan char_traits berarti semua perbandingan Anda tidak peka terhadap huruf besar-kecil, yang biasanya tidak seperti yang Anda inginkan.

Ini sudah cukup. Itu harus cukup efisien. Tidak menangani unicode atau apapun.

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

Pembaruan: Versi Bonus C ++ 14 ( #include <algorithm>):

bool iequals(const string& a, const string& b)
{
    return std::equal(a.begin(), a.end(),
                      b.begin(), b.end(),
                      [](char a, char b) {
                          return tolower(a) == tolower(b);
                      });
}
Timmmm
sumber
27
Sebenarnya, pustaka string boost adalah pustaka header saja, jadi tidak perlu menautkan apa pun. Selain itu, Anda dapat menggunakan utilitas 'bcp' boost untuk menyalin hanya header string ke hierarki sumber Anda, jadi Anda tidak perlu memerlukan pustaka boost penuh.
Gretchen
Ah saya tidak tahu tentang bcp, itu terlihat sangat berguna. Terimakasih atas infonya!
Timmmm
9
Baik untuk mengetahui versi sederhana dan tidak-meningkatkan-ketergantungan.
Deqing
2
@Anna Text Library dari boost perlu dibangun dan ditautkan. Itu menggunakan IBM ICU.
Behrouz.M
Juga tersedia dengan C ++ 11
martian
58

Jika Anda menggunakan sistem POSIX, Anda dapat menggunakan strcasecmp . Namun, fungsi ini bukan bagian dari standar C, juga tidak tersedia di Windows. Ini akan melakukan perbandingan case-insensitive pada karakter 8-bit, selama lokalnya adalah POSIX. Jika lokal bukan POSIX, hasilnya tidak terdefinisi (sehingga mungkin melakukan perbandingan lokal, atau mungkin tidak). Setara karakter lebar tidak tersedia.

Kegagalan itu, sejumlah besar implementasi C library bersejarah memiliki fungsi stricmp () dan strnicmp (). Visual C ++ pada Windows mengganti nama semua ini dengan mengawali mereka dengan garis bawah karena mereka bukan bagian dari standar ANSI, jadi pada sistem itu mereka disebut _stricmp atau _strnicmp . Beberapa perpustakaan mungkin juga memiliki fungsi setara karakter lebar atau multibyte (biasanya dinamakan misalnya wcsicmp, mbcsicmp, dan sebagainya).

C dan C ++ sama-sama tidak mengetahui masalah internasionalisasi, jadi tidak ada solusi yang baik untuk masalah ini, kecuali menggunakan perpustakaan pihak ketiga. Periksa IBM ICU (Komponen Internasional untuk Unicode) jika Anda memerlukan pustaka yang kuat untuk C / C ++. ICU adalah untuk sistem Windows dan Unix.

Taman Derek
sumber
53

Apakah Anda berbicara tentang membandingkan kasus bodoh bodoh atau Unicode penuh normalisasi?

Membandingkan bodoh tidak akan menemukan string yang mungkin sama tetapi tidak sama biner.

Contoh:

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

Semuanya setara tetapi mereka juga memiliki representasi biner yang berbeda.

Yang mengatakan, Normalisasi Unicode harus menjadi bacaan wajib terutama jika Anda berencana mendukung Hangul, Thailand dan bahasa asia lainnya.

Selain itu, IBM mematenkan algoritma Unicode yang paling optimal dan membuatnya tersedia untuk umum. Mereka juga mempertahankan implementasi: IBM ICU

Coincoin
sumber
2
Anda mungkin ingin mengedit tautan ICU itu ke site.icu-project.org
DevSolar
31

boost :: iequals tidak kompatibel dengan utf-8 dalam hal string. Anda dapat menggunakan boost :: locale .

comparator<char,collator_base::secondary> cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;
  • Utama - abaikan aksen dan huruf besar-kecil, membandingkan huruf dasar saja. Misalnya "fasad" dan "Fasad" adalah sama.
  • Sekunder - abaikan case character tetapi pertimbangkan aksen. "fasad" dan "fasad" berbeda tetapi "fasad" dan "fasad" sama.
  • Tersier - pertimbangkan kasus dan aksen: "Façade" dan "façade" berbeda. Abaikan tanda baca.
  • Kuarter - pertimbangkan semua huruf besar, aksen, dan tanda baca. Kata-kata harus identik dalam hal representasi Unicode.
  • Identik - sebagai kuaterner, tetapi bandingkan poin kode juga.
Igor Milyakov
sumber
30

Pikiran pertama saya untuk versi non-unicode adalah melakukan sesuatu seperti ini:


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

Anda dapat menggunakan strcasecmpdi Unix, atau stricmpdi Windows.

Satu hal yang belum disebutkan sejauh ini adalah bahwa jika Anda menggunakan string stl dengan metode ini, ada baiknya untuk terlebih dahulu membandingkan panjang kedua string, karena informasi ini sudah tersedia untuk Anda di kelas string. Ini bisa mencegah melakukan perbandingan string yang mahal jika dua string yang Anda bandingkan bahkan tidak sama panjangnya.

bradtgmurray
sumber
Karena menentukan panjang string terdiri dari iterasi setiap karakter dalam string dan membandingkannya dengan 0, apakah benar-benar ada banyak perbedaan antara itu dan hanya membandingkan string segera? Saya kira Anda mendapatkan memori lokalitas yang lebih baik dalam kasus di mana kedua string tidak cocok, tetapi mungkin hampir 2x runtime dalam kasus pertandingan.
uliwitness
3
C ++ 11 menetapkan bahwa kompleksitas std :: string :: length harus konstan: cplusplus.com/reference/string/string/length
bradtgmurray
1
Itu fakta kecil yang menyenangkan, tetapi tidak banyak berpengaruh di sini. strcasecmp () dan stricmp () keduanya menggunakan string C yang tidak didekorasi, sehingga tidak ada string std :: string yang terlibat.
uliwitness
3
Metode ini akan mengembalikan -1 jika Anda membandingkan "a" vs "ab". Panjangnya berbeda tetapi "a" muncul sebelum "ab". Jadi, hanya membandingkan panjang tidak layak jika penelepon peduli tentang pemesanan.
Nathan
13

Saya mencoba menyusun jawaban yang baik dari semua posting, jadi bantu saya edit ini:

Berikut adalah metode untuk melakukan ini, meskipun itu mengubah string, dan tidak ramah Unicode, itu harus portabel yang merupakan nilai tambah:

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

Dari apa yang saya baca ini lebih portabel daripada stricmp () karena stricmp () sebenarnya bukan bagian dari perpustakaan std, tetapi hanya diimplementasikan oleh sebagian besar vendor kompiler.

Untuk mendapatkan implementasi yang benar-benar ramah Unicode tampaknya Anda harus pergi ke luar perpustakaan std. Satu perpustakaan pihak ketiga yang bagus adalah IBM ICU (Komponen Internasional untuk Unicode)

Juga meningkatkan :: iequals menyediakan utilitas yang cukup baik untuk melakukan perbandingan semacam ini.

Adam
sumber
dapatkah Anda memberi tahu, apa artinya :: tolower, mengapa Anda bisa menggunakan tolower alih-alih tolower (), dan apa itu '::' sebelumnya? terima kasih
VextoR
17
Ini bukan solusi yang sangat efisien - Anda membuat salinan dari kedua string dan mengubah semuanya bahkan jika karakter pertama berbeda.
Timmmm
2
Jika Anda akan tetap membuat salinan, mengapa tidak memberikan nilai alih-alih dengan referensi?
celticminstrel
Saya pikir ini tip sederhana tanpa dorongan. :)
cmcromance
1
pertanyaannya diajukan secara eksplisit untuk tidak transformseluruh string sebelum perbandingan
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);})

Anda dapat menggunakan kode di atas dalam C ++ 14 jika Anda tidak berada dalam posisi untuk menggunakan boost. Anda harus menggunakan std::towloweruntuk karakter lebar.

anggur
sumber
4
Saya pikir Anda perlu menambahkan a str1.size() == str2.size() &&ke depan sehingga tidak akan keluar batas ketika str2 adalah awalan dari str1.
ɲeuroburɳ
11

The Boost.String perpustakaan memiliki banyak algoritma untuk melakukan hal-insenstive perbandingan dan sebagainya.

Anda bisa menerapkan sendiri, tetapi mengapa repot ketika itu sudah dilakukan?

Dean Harding
sumber
1
Tidak ada cara built-in dengan std :: string?
WilliamKF
6
Tidak, tidak ada.
Dean Harding
3
"... kenapa repot-repot kalau sudah dilakukan?" - bagaimana jika Anda tidak menggunakan Peningkatan? OP tidak memiliki tag dengan pertanyaan.
jww
11

FYI, strcmp()dan stricmp()rentan terhadap buffer overflow, karena mereka hanya memproses sampai mereka mencapai terminator nol. Lebih aman digunakan _strncmp()dan _strnicmp().

Baji
sumber
6
Benar, meskipun overREADing buffer secara signifikan lebih berbahaya daripada overWRITEing buffer.
Adam Rosenfield
4
stricmp()dan strnicmp()bukan bagian dari standar POSIX :-( Namun Anda dapat menemukan strcasecmp(), strcasecmp_l(), strncasecmp()dan strncasecmp_l()di POSIX sundulan strings.h:-) melihat opengroup.org
olibre
2
@AdamRosenfield 'lebih buruk' tergantung pada konteks. Dalam keamanan, kadang-kadang inti dari overwrite adalah untuk mendapatkan overread.
karmakaze
10

Lihat 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
sumber
1
Metode ini berpotensi tidak aman dan tidak portabel. std::tolowerberfungsi hanya jika karakternya dikodekan ASCII. Tidak ada jaminan seperti itu untuk std::string- sehingga perilaku itu tidak dapat didefinisikan dengan mudah.
plasmacel
@plasmacel Kemudian gunakan fungsi yang bekerja dengan penyandian lainnya.
Brian Rodriguez
9

Untuk kebutuhan perbandingan string case sensitif dasar saya, saya lebih suka tidak harus menggunakan perpustakaan eksternal, saya juga tidak ingin kelas string terpisah dengan sifat-sifat case sensitif yang tidak kompatibel dengan semua string saya yang lain.

Jadi yang saya pikirkan adalah ini:

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

Fungsi sederhana dengan satu overload untuk char dan lainnya untuk whar_t. Tidak menggunakan apa pun yang tidak standar sehingga harus baik-baik saja pada platform apa pun.

Perbandingan kesetaraan tidak akan mempertimbangkan masalah seperti pengodean panjang variabel dan normalisasi Unicode, tetapi basic_string tidak memiliki dukungan untuk itu yang saya sadari dan itu biasanya bukan masalah.

Dalam kasus di mana manipulasi leksikografis yang lebih canggih dari teks diperlukan, maka Anda hanya harus menggunakan perpustakaan pihak ketiga seperti Boost, yang diharapkan.

Neutrino
sumber
2
Anda mungkin bisa membuat satu fungsi itu jika Anda membuatnya menjadi templat dan menggunakan basic_string <T> alih-alih versi string / wstring yang terpisah?
uliwitness
2
Bagaimana templat fungsi tunggal memohon baik toupper atau towupper tanpa menggunakan spesialisasi atau makro, fungsi yang berlebihan tampak seperti implementasi yang lebih sederhana dan lebih tepat daripada keduanya.
Neutrino
9

Pendek dan bagus. Tidak ada dependensi lain, selain std C lib diperpanjang .

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

mengembalikan true jika str1dan str2sama. strcasecmpmungkin tidak ada, mungkin ada analog stricmp, strcmpi, dll

Kode contoh:

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

Keluaran:

true
true
true
true
true
Kyb
sumber
6
aneh bahwa C ++ std :: string tidak memiliki metode perbandingan kasus-abaikan ..
kyb
1
"strcasecmp bukan bagian dari standar" - Mark Ransom 1 Desember '14 pada 19:57
Liviu
ya, tetapi sebagian besar kompiler modern memilikinya atau yang lain bernama analog. stricmp, strcmpi, strcasecmp, Dll Terima kasih. pesan diedit.
Kyb
TODO: gunakan cout << boolalphadaripada saya bool2strkarena Ini untuk secara implisit mengkonversi bool ke karakter untuk streaming.
kyb
Ada di <strings.h> di perpustakaan gcc.
Burung hantu
7

Melakukan ini tanpa menggunakan Boost dapat dilakukan dengan mendapatkan pointer string C dengan c_str()dan menggunakan strcasecmp:

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

Dengan asumsi Anda sedang mencari metode dan bukan fungsi sihir yang sudah ada, terus terang tidak ada cara yang lebih baik. Kita semua dapat menulis cuplikan kode dengan trik pintar untuk rangkaian karakter terbatas, tetapi pada akhirnya di suatu tempat Anda harus mengonversi karakter.

Pendekatan terbaik untuk konversi ini adalah melakukannya sebelum perbandingan. Ini memungkinkan Anda banyak fleksibilitas dalam hal skema penyandian, yang seharusnya tidak diperhatikan oleh operator perbandingan Anda.

Tentu saja Anda dapat 'menyembunyikan' konversi ini di belakang fungsi atau kelas string Anda sendiri, tetapi Anda masih perlu mengonversi string sebelum perbandingan.

Andrew Grant
sumber
6

Saya menulis versi char_traits case-insensitive untuk digunakan dengan std :: basic_string untuk menghasilkan string std :: yang tidak peka huruf besar-kecil ketika melakukan perbandingan, pencarian, dll menggunakan fungsi anggota std :: basic_string bawaan.

Jadi dengan kata lain, saya ingin melakukan sesuatu seperti ini.

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

assert( a == b );

... yang std :: string tidak dapat menangani. Inilah penggunaan char_traits baru saya:

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

assert( a == b );

... dan inilah implementasinya:

/*  ---

        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
sumber
2
Ini berfungsi untuk karakter reguler, tetapi tidak akan berfungsi untuk semua Unicode, karena kapitalisasi tidak harus dua arah (ada contoh yang baik dalam bahasa Yunani yang melibatkan sigma yang tidak dapat saya ingat sekarang; sesuatu seperti itu memiliki dua huruf kecil dan huruf besar satu , dan Anda tidak bisa mendapatkan perbandingan yang tepat)
coppro
1
Itu benar-benar cara yang salah untuk melakukannya. Sensitivitas huruf tidak boleh menjadi properti dari string itu sendiri. Apa yang terjadi ketika objek string yang sama membutuhkan perbandingan case-sensitive dan case-sensitive?
Ferruccio
Jika sensitivitas huruf tidak sesuai untuk menjadi "bagian dari" string, maka fungsi find () sama sekali tidak. Yang mana, bagi Anda, mungkin benar, dan itu tidak masalah. IMO hal terbesar tentang C ++ adalah ia tidak memaksakan paradigma tertentu pada programmer. Ini adalah apa yang Anda inginkan / inginkan.
John Dibling
Sebenarnya, saya pikir sebagian besar C + + - guru (seperti yang ada di komite standar) setuju bahwa itu adalah kesalahan untuk menempatkan find () di std :: basic_string <> bersama dengan banyak hal lain yang bisa ditempatkan di fungsi gratis. Selain itu ada beberapa masalah dengan memasukkannya ke dalam tipe.
Andreas Magnusson
Seperti yang telah ditunjukkan orang lain, ada dua hal utama yang salah dengan solusi ini (ironisnya, satu adalah antarmuka dan yang lainnya adalah implementasinya ;-)).
Konrad Rudolph
4

Saya memiliki pengalaman yang baik dalam menggunakan Komponen Internasional untuk pustaka Unicode - mereka sangat kuat, dan menyediakan metode untuk konversi, dukungan lokal, rendering tanggal dan waktu, pemetaan kasus (yang sepertinya tidak Anda inginkan), dan pengumpulan , yang mencakup perbandingan case-and-accentitive (dan banyak lagi). Saya hanya menggunakan versi C ++ dari pustaka, tetapi mereka tampaknya memiliki versi Java juga.

Ada metode untuk melakukan pembandingan yang dinormalisasi sebagaimana dimaksud oleh @Coincoin, dan bahkan dapat menjelaskan lokal - misalnya (dan ini contoh penyortiran, tidak sepenuhnya kesetaraan), secara tradisional dalam bahasa Spanyol (di Spanyol), kombinasi huruf "ll" akan memilah antara "l" dan "m", jadi "lz" <"ll" <"ma".

Blair Conrad
sumber
4

Cukup gunakan strcmp()untuk case sensitive dan strcmpi()atau stricmp()untuk case sensitive case. Yang keduanya di file header<string.h>

format:

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

Pemakaian:

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;

Keluaran

apel dan ApPlE adalah sama

a datang sebelum b, jadi apel datang sebelum bola

Reubenjohn
sumber
2
Mengundurkan diri karena ini bukan cara C ++ dalam melakukan sesuatu.
Thomas Daugaard
Ini adalah konvensi c ++ di universitas saya tetapi saya akan mengingatnya saat memposting di sini
reubenjohn
4
stricmp adalah ekstensi Microsoft AFAIK. BSD tampaknya memiliki strcasecmp () sebagai gantinya.
uliwitness
3

Terlambat ke pesta, tetapi di sini ada varian yang menggunakan std::locale, dan dengan demikian menangani Turki dengan benar:

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

memberi Anda functor yang menggunakan lokal aktif untuk mengonversi karakter menjadi huruf kecil, yang kemudian dapat Anda gunakan std::transformuntuk menghasilkan string huruf kecil:

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

Ini juga berfungsi untuk wchar_tstring berbasis.

Simon Richter
sumber
2

Hanya catatan tentang metode apa pun yang akhirnya Anda pilih, jika metode itu terjadi termasuk penggunaan strcmp yang beberapa jawaban sarankan:

strcmptidak bekerja dengan data Unicode secara umum. Secara umum, itu bahkan tidak bekerja dengan encode Unicode berbasis byte, seperti utf-8strcmp hanya membuat perbandingan byte per byte dan titik kode Unicode yang dikodekan dalam utf-8 dapat membutuhkan lebih dari 1 byte. Satu-satunya kasus Unicode khusus yang strcmpditangani dengan benar adalah ketika string yang dikodekan dengan pengkodean berbasis byte hanya berisi poin kode di bawah U + 00FF - maka perbandingan byte-per-byte sudah cukup.

Johann Gerell
sumber
2

Pada awal 2013, proyek ICU, dikelola oleh IBM, adalah jawaban yang cukup bagus untuk ini.

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

ICU adalah "perpustakaan Unicode portabel yang lengkap yang secara ketat melacak standar industri." Untuk masalah spesifik perbandingan string, objek Collation melakukan apa yang Anda inginkan.

Proyek Mozilla mengadopsi ICU untuk internasionalisasi di Firefox pada pertengahan 2012; Anda dapat melacak diskusi teknik, termasuk masalah sistem pembangunan dan ukuran file data, di sini:

michaelhanson
sumber
2

Sepertinya solusi di atas tidak menggunakan metode bandingkan dan menerapkan total lagi jadi di sini adalah solusi saya dan berharap itu berfungsi untuk Anda (Ini berfungsi dengan baik).

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

Jika Anda tidak ingin menggunakan perpustakaan Boost maka di sini ada solusi untuk itu hanya menggunakan header C ++ io standar.

#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
sumber
Saya percaya std :: toupper ada di #include <cctype>, Anda mungkin perlu memasukkannya.
David Ledger
Jika Anda akan menggunakan versi global seperti ini :: toupper maka Anda mungkin tidak perlu memasukkan <ctype> karena ada dua versi versi c dan versi c ++ dengan lokal kurasa. Jadi lebih baik menggunakan versi global ":: toupper ()"
HaSeeB MiR
solusi ini gagal ketika salah satu string kosong: "" - itu mengembalikan true dalam kasus ketika itu harus mengembalikan false
ekkis
0

Jika Anda harus membandingkan string sumber lebih sering dengan string lain, salah satu solusi elegan adalah menggunakan 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);
smibe
sumber
Mencoba ini tetapi kompilasi kesalahan: error: conversion from 'const char [5]' to non-scalar type 'std::wstring {aka std::basic_string<wchar_t>}' requested
Deqing
ide buruk. Itu solusi terburuk.
Behrouz.M
Ini bukan solusi yang baik, tetapi bahkan jika Anda ingin menggunakannya, Anda memerlukan L di depan konstanta widestring Anda, misalnya L "TEST"
celticminstrel
Akan lebih baik jika seseorang bisa menjelaskan mengapa itu adalah solusi terburuk. Karena masalah kinerja? Membuat regex itu mahal, tetapi setelah itu perbandingannya harus sangat cepat.
smibe
itu bisa digunakan dan portabel, masalah utama adalah yang pertama tidak dapat berisi karakter yang digunakan regex. Itu tidak dapat digunakan sebagai perbandingan string umum karena itu. Ini juga akan lebih lambat, ada bendera untuk membuatnya berfungsi seperti yang dikatakan smibe tetapi masih tidak dapat digunakan sebagai fungsi umum.
Ben
0

Cara sederhana untuk membandingkan dua string dalam c ++ (diuji untuk windows) adalah menggunakan _stricmp

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

Jika Anda ingin menggunakan dengan std :: string, contoh:

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

Untuk informasi lebih lanjut di sini: https://msdn.microsoft.com/it-it/library/e0z9k731.aspx

Ayo
sumber
Ada baiknya membaca stackoverflow.com/a/12414441/95309 selain jawaban ini, karena ini a) fungsi C, dan b) seharusnya tidak portabel.
Claus Jørgensen
#apa sertakan yang perlu kita buat agar berhasil?
ekkis
1
@ekkis untuk menggunakan _stricmp Anda harus memasukkan <string.h> seperti yang Anda baca di sini: 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;
        }
      }
}

ini mungkin bisa dibuat jauh lebih efisien, tetapi di sini ada versi besar dengan semua bitnya telanjang.

tidak semua yang portabel, tetapi bekerja dengan baik dengan apa pun yang ada di komputer saya (tidak tahu, saya gambar bukan kata-kata)

pengguna4578093
sumber
Ini bukan dukungan Unicode yang merupakan pertanyaan yang diajukan.
Behrouz.M
Ini tidak mendukung rangkaian karakter non-Inggris.
Robert Andrzantai
-3

Cara mudah untuk membandingkan string yang hanya berbeda dengan huruf kecil dan huruf besar adalah dengan melakukan perbandingan ascii. Semua huruf besar dan kecil berbeda 32 bit dalam tabel ascii, menggunakan informasi ini kami memiliki ...

    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
sumber
3
Menurut ini, "++ j" akan ditemukan sama dengan "KKJ", dan "1234" akan ditemukan sama dengan "QRST". Saya ragu itu adalah sesuatu yang diinginkan siapa pun.
celticminstrel