So sánh chuỗi không phân biệt chữ hoa chữ thường trong C ++ [đã đóng]


373

Cách tốt nhất để thực hiện so sánh chuỗi không phân biệt chữ hoa chữ thường trong C ++ mà không chuyển đổi một chuỗi thành tất cả chữ hoa hoặc tất cả chữ thường là gì?

Vui lòng cho biết liệu các phương thức có thân thiện với Unicode và mức độ di động của chúng hay không.


@ [Adam] (# 11679): Mặc dù biến thể này tốt về khả năng sử dụng nhưng nó lại kém về mặt hiệu suất vì nó tạo ra các bản sao không cần thiết. Tôi có thể bỏ qua một cái gì đó nhưng tôi tin rằng cách tốt nhất (không phải là Unicode) là sử dụng std::stricmp. Nếu không, hãy đọc những gì Herb nói .
Konrad Rudolph

Trong c, người ta thường buộc phải kéo toàn bộ chuỗi sau đó so sánh theo cách đó - hoặc cuộn so sánh của riêng bạn: P
Michael Dorgan

một câu hỏi sau có câu trả lời đơn giản hơn: strcasecmp (ít nhất là cho trình biên dịch BSD & POSIX) stackoverflow.com/questions/9182912/
Lỗi

@ Mσᶎ câu hỏi này cũng có câu trả lời đó, với cảnh báo quan trọng strcasecmpkhông phải là một phần của tiêu chuẩn và bị thiếu trong ít nhất một trình biên dịch phổ biến.
Đánh dấu tiền chuộc

Câu trả lời:


317

Boost bao gồm một thuật toán tiện dụng cho việc này:

#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
UTF-8 này có thân thiện không? Tôi nghĩ là không.
vladr

18
Không, bởi vì UTF-8 cho phép các chuỗi giống hệt nhau được mã hóa bằng các mã nhị phân khác nhau, do các dấu, kết hợp, các vấn đề về giá
thầu

10
@ vy32 Điều đó hoàn toàn không chính xác! Các kết hợp UTF-8 là loại trừ lẫn nhau. Nó phải luôn luôn sử dụng biểu diễn ngắn nhất có thể, nếu không, đó là một chuỗi UTF-8 không đúng định dạng hoặc điểm mã phải được xử lý cẩn thận.
Wiz

48
@Wiz, bạn đang bỏ qua vấn đề chuẩn hóa chuỗi Unicode. ñ có thể được biểu diễn dưới dạng kết hợp ˜ theo sau là n hoặc với ký tự. Bạn cần sử dụng chuẩn hóa chuỗi Unicode trước khi thực hiện comparaison. Vui lòng xem lại Báo cáo kỹ thuật Unicode # 15, unicode.org/reports/tr15
vy32

12
@wonkorealtime: vì "ß" được chuyển đổi thành chữ hoa là "SS": fileformat.info/info/unicode/char/df/index.htmlm
Vịt Mooing

118

Tận dụng tiêu chuẩn char_traits. Hãy nhớ lại rằng a std::stringthực tế là một typedef cho std::basic_string<char>, hoặc rõ ràng hơn , std::basic_string<char, std::char_traits<char> >. Cácchar_traits loại mô tả cách nhân vật so sánh, làm thế nào họ sao chép, làm thế nào họ đúc vv Tất cả bạn cần làm là typedef một chuỗi mới hơn basic_string, và cung cấp cho nó với tùy chỉnh của riêng bạn char_traitsmà so sánh trường hợp 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;

Các chi tiết có trong phần của Tuần lễ số 29 .


10
Theo như tôi biết từ thử nghiệm của riêng tôi, điều này làm cho loại chuỗi mới của bạn không tương thích với chuỗi std ::.
Zan Lynx

8
Tất nhiên là có - vì lợi ích của chính nó. Một chuỗi không phân biệt chữ hoa chữ thường là một cái gì đó khác : typedef std::basic_string<char, ci_char_traits<char> > istring, không typedef std::basic_string<char, std::char_traits<char> > string.
Andreas Spindler

232
"Tất cả những gì bạn cần làm ..."
Tim MB

3
@Nathan có thể sử dụng trình biên dịch có thể thực hiện CSE cơ bản trên mã ...
The Croagant Paramag từ

17
Bất kỳ ngôn ngữ xây dựng nào buộc sự điên rồ như vậy trong trường hợp tầm thường này nên và có thể bị từ bỏ mà không hối tiếc.
Erik Aronesty

86

Rắc rối với boost là bạn phải liên kết và phụ thuộc vào boost. Không dễ dàng trong một số trường hợp (ví dụ Android).

Và sử dụng char_traits có nghĩa là tất cả các so sánh của bạn không phân biệt chữ hoa chữ thường, đây không phải là điều bạn muốn.

Điều này là đủ. Nó nên được hiệu quả hợp lý. Không xử lý unicode hoặc bất cứ điều gì mặc dù.

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

Cập nhật: Phần thưởng C ++ 14 phiên bản ( #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
Trên thực tế, thư viện chuỗi boost là một thư viện chỉ tiêu đề, do đó không cần phải liên kết với bất cứ điều gì. Ngoài ra, bạn có thể sử dụng tiện ích 'bcp' của boost để sao chép chỉ các tiêu đề chuỗi vào cây nguồn của bạn, vì vậy bạn không cần phải yêu cầu thư viện boost đầy đủ.
Gretchen

Ah tôi không biết về bcp, nó có vẻ rất hữu ích. Cảm ơn bạn về thông tin!
Timmmm

9
Tốt để biết một phiên bản đơn giản và không tăng phụ thuộc.
Đức Thanh

2
@Anna Thư viện văn bản của boost cần được xây dựng và liên kết. Nó sử dụng ICU của IBM.
Behrouz.M

Cũng có sẵn với C ++ 11
martian

58

Nếu bạn đang sử dụng hệ thống POSIX, bạn có thể sử dụng strcasecmp . Tuy nhiên, chức năng này không phải là một phần của tiêu chuẩn C, cũng không có sẵn trên Windows. Điều này sẽ thực hiện so sánh không phân biệt chữ hoa chữ thường trên ký tự 8 bit, miễn là miền địa phương là POSIX. Nếu miền địa phương không phải là POSIX, kết quả không được xác định (do đó, nó có thể thực hiện so sánh cục bộ hoặc có thể không). Một tương đương rộng ký tự là không có sẵn.

Không, một số lượng lớn các triển khai thư viện C lịch sử có các hàm stricmp () và strnicmp (). Visual C ++ trên Windows đã đổi tên tất cả những thứ này bằng cách thêm tiền tố vào chúng bằng dấu gạch dưới vì chúng không phải là một phần của tiêu chuẩn ANSI, vì vậy trên hệ thống đó chúng được gọi là _stricmp hoặc _strnicmp . Một số thư viện cũng có thể có các hàm tương đương ký tự rộng hoặc đa nhân (thường được đặt tên là wcsicmp, mbcsicmp, v.v.).

C và C ++ hầu như không biết gì về các vấn đề quốc tế hóa, vì vậy không có giải pháp tốt cho vấn đề này, ngoại trừ việc sử dụng thư viện của bên thứ ba. Hãy xem IBM ICU (Các thành phần quốc tế cho Unicode) nếu bạn cần một thư viện mạnh mẽ cho C / C ++. ICU dành cho cả hệ thống Windows và Unix.


53

Bạn đang nói về một trường hợp ngu ngốc so sánh không nhạy cảm hoặc so sánh Unicode bình thường hóa hoàn toàn?

Một so sánh ngu ngốc sẽ không tìm thấy các chuỗi có thể giống nhau nhưng không phải là nhị phân bằng nhau.

Thí dụ:

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

Tất cả đều tương đương nhưng chúng cũng có các biểu diễn nhị phân khác nhau.

Điều đó nói rằng, Chuẩn hóa Unicode phải là một cách đọc bắt buộc đặc biệt nếu bạn có kế hoạch hỗ trợ Hangul, Thaï và các ngôn ngữ châu Á khác.

Ngoài ra, IBM đã cấp bằng sáng chế hầu hết các thuật toán Unicode được tối ưu hóa và cung cấp chúng công khai. Họ cũng duy trì triển khai: IBM ICU


2
Bạn có thể muốn chỉnh sửa liên kết ICU đó đến site.icu-project.org
DevSolar

31

boost :: iequals không tương thích utf-8 trong trường hợp chuỗi. Bạn có thể sử dụng boost :: locale .

comparator<char,collator_base::secondary> cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;
  • Chính - bỏ qua dấu và trường hợp ký tự, chỉ so sánh các chữ cái cơ sở. Ví dụ: "mặt tiền" và "Mặt tiền" là như nhau.
  • Thứ cấp - bỏ qua trường hợp nhân vật nhưng xem xét các dấu. "Mặt tiền" và "mặt tiền" khác nhau nhưng "Mặt tiền" và "mặt tiền" là như nhau.
  • Đệ tam - xem xét cả trường hợp và dấu: "Mặt tiền" và "mặt tiền" là khác nhau. Bỏ qua dấu câu.
  • Đệ tứ - xem xét tất cả các trường hợp, dấu và dấu chấm câu. Các từ phải giống hệt nhau về mặt đại diện Unicode.
  • Đồng nhất - như là bậc bốn, nhưng so sánh các điểm mã là tốt.

30

Suy nghĩ đầu tiên của tôi về một phiên bản không unicode là làm một cái gì đó như thế này:


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

Bạn có thể sử dụng strcasecmptrên Unix hoặcstricmp trên Windows.

Một điều chưa được đề cập cho đến nay là nếu bạn đang sử dụng chuỗi stl với các phương thức này, trước tiên, việc so sánh độ dài của hai chuỗi là rất hữu ích, vì thông tin này đã có sẵn cho bạn trong lớp chuỗi. Điều này có thể ngăn việc thực hiện so sánh chuỗi tốn kém nếu hai chuỗi bạn đang so sánh thậm chí không có cùng độ dài ở vị trí đầu tiên.


Vì việc xác định độ dài của chuỗi bao gồm lặp lại trên mỗi ký tự trong chuỗi và so sánh nó với 0, có thực sự có nhiều khác biệt giữa điều đó và chỉ cần so sánh các chuỗi ngay lập tức? Tôi đoán bạn có được địa phương bộ nhớ tốt hơn trong trường hợp cả hai chuỗi không khớp, nhưng có lẽ gần gấp đôi thời gian chạy trong trường hợp khớp.
uliwitness

3
C ++ 11 mà quy định cụ thể mức độ phức tạp của std :: string :: chiều dài phải được liên tục: cplusplus.com/reference/string/string/length
bradtgmurray

1
Đó là một thực tế nhỏ vui vẻ, nhưng có ít mang ở đây. strcasecmp () và stricmp () đều lấy các chuỗi C không được trang trí, do đó không có chuỗi std :: liên quan.
uliwitness

3
Các phương thức này sẽ trả về -1 nếu bạn so sánh "a" với "ab". Độ dài khác nhau nhưng "a" xuất hiện trước "ab". Vì vậy, chỉ đơn giản là so sánh độ dài là không khả thi nếu người gọi quan tâm đến việc đặt hàng.
Nathan


13

Tôi đang cố gắng đưa ra một câu trả lời hay từ tất cả các bài viết, vì vậy hãy giúp tôi chỉnh sửa điều này:

Đây là một phương pháp để làm điều này, mặc dù nó biến đổi các chuỗi và không thân thiện với Unicode, nhưng nó có thể mang theo được là một ưu điểm:

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

Từ những gì tôi đã đọc, nó dễ mang theo hơn stricmp () vì stricmp () thực tế không phải là một phần của thư viện std, mà chỉ được thực hiện bởi hầu hết các nhà cung cấp trình biên dịch.

Để có được một triển khai thực sự thân thiện với Unicode, có vẻ như bạn phải ra ngoài thư viện tiêu chuẩn. Một thư viện bên thứ 3 tốt là ICU của IBM (Các thành phần quốc tế cho Unicode)

Ngoài ra boost :: iequals cung cấp một tiện ích khá tốt để thực hiện loại so sánh này.


bạn có thể vui lòng cho biết, điều gì có nghĩa là :: khoan dung, tại sao bạn có thể sử dụng dung sai thay vì dung sai () và '::' trước đó là gì? cảm ơn
VextoR

17
Đây không phải là một giải pháp rất hiệu quả - bạn tạo các bản sao của cả hai chuỗi và biến đổi tất cả chúng ngay cả khi ký tự đầu tiên khác nhau.
Timmmm

2
Nếu bạn định tạo một bản sao, tại sao không chuyển theo giá trị thay vì tham chiếu?
celticminstrel

Tôi nghĩ rằng đó là mẹo đơn giản mà không cần tăng. :)
cmcromance 8/11/2016

1
câu hỏi yêu cầu rõ ràng không phải transformtoàn bộ chuỗi trước khi so sánh
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);})

Bạn có thể sử dụng đoạn mã trên trong C ++ 14 nếu bạn không ở vị trí để sử dụng boost. Bạn phải sử dụng std::towlowercho ký tự rộng.


4
Tôi nghĩ bạn cần thêm a str1.size() == str2.size() &&vào phía trước để không bị vượt quá giới hạn khi str2 là tiền tố của str1.
ɲeuroburɳ

11

Các Boost.String thư viện có rất nhiều thuật toán để thực hiện so sánh trường insenstive và vân vân.

Bạn có thể tự thực hiện, nhưng tại sao phải bận tâm khi nó đã được thực hiện?


1
Không có cách nào tích hợp với std :: string?
WilliamKF

6
Không, không có.
Dean Harding

3
"... tại sao phải bận tâm khi nó đã được thực hiện?" - nếu bạn không sử dụng Boost thì sao? OP không có thẻ với câu hỏi.
jww

11

FYI, strcmp()stricmp()dễ bị tràn bộ đệm, vì chúng chỉ xử lý cho đến khi chúng chạm vào một bộ kết thúc null. Nó an toàn hơn để sử dụng _strncmp()_strnicmp().


6
Đúng, mặc dù overREADing một bộ đệm ít nguy hiểm hơn đáng kể so với overWRITEing một bộ đệm.
Adam Rosenfield

4
stricmp()strnicmp()không nằm trong tiêu chuẩn POSIX :-( Tuy nhiên bạn có thể tìm thấy strcasecmp(), strcasecmp_l(), strncasecmp()strncasecmp_l()trong POSIX tiêu đề strings.h:-) thấy opengroup.org
olibre

2
@AdamRosenfield 'tệ hơn' tùy thuộc vào ngữ cảnh. Trong bảo mật, đôi khi toàn bộ quan điểm của một ghi đè là để đọc quá mức.
karmakaze

10

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

Bản giới thiệu


Phương pháp này có khả năng không an toàn và không di động. std::tolowerchỉ hoạt động nếu ký tự được mã hóa ASCII. Không có sự đảm bảo nào cho std::string- vì vậy nó có thể là hành vi không xác định dễ dàng.
plasmacel

@plasmacel Sau đó sử dụng hàm hoạt động với các bảng mã khác.
Brian Rodriguez

9

Đối với nhu cầu so sánh chuỗi không nhạy cảm trường hợp cơ bản của tôi, tôi không muốn phải sử dụng thư viện bên ngoài, tôi cũng không muốn một lớp chuỗi riêng biệt với các đặc điểm không nhạy cảm trường hợp không tương thích với tất cả các chuỗi khác của tôi.

Vì vậy, những gì tôi đã đưa ra là đây:

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

Một hàm đơn giản với một quá tải cho char và một hàm khác cho whar_t. Không sử dụng bất cứ thứ gì không chuẩn nên sẽ ổn trên mọi nền tảng.

Việc so sánh bằng sẽ không xem xét các vấn đề như mã hóa độ dài thay đổi và chuẩn hóa Unicode, nhưng basic_ chuỗi không hỗ trợ cho điều đó mà tôi biết về dù sao và nó thường không phải là vấn đề.

Trong trường hợp yêu cầu thao tác văn bản từ vựng phức tạp hơn, bạn chỉ cần sử dụng thư viện của bên thứ ba như Boost, điều này sẽ được mong đợi.


2
Bạn có thể có thể tạo một chức năng đó nếu bạn biến nó thành một mẫu và sử dụng basic_ chuỗi <T> thay vì các phiên bản chuỗi / chuỗi riêng biệt?
uliwitness

2
Làm thế nào mẫu hàm đơn sẽ gọi toupper hoặc Towupper mà không cần sử dụng chuyên môn hóa hoặc macro, quá tải hàm có vẻ như là một triển khai đơn giản và phù hợp hơn cả.
Neutrino

9

Ngắn và đẹp. Không phụ thuộc khác, ngoài mở rộng std C lib.

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

trả về true nếu str1str2bằng nhau. strcasecmpcó thể không tồn tại, có thể có chất tương tự stricmp, strcmpivv

Mã ví dụ:

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

Đầu ra:

true
true
true
true
true

6
Thật kỳ lạ khi C ++ std :: string không có phương thức so sánh trường hợp bỏ qua ..
kyb

1
"strcasecmp không phải là một phần của tiêu chuẩn" - Mark Ransom ngày 1 tháng 12 năm 14 lúc 19:57
Liviu

đúng, nhưng hầu hết các trình biên dịch hiện đại đều có nó hoặc một cái tên tương tự khác. stricmp, strcmpi, strcasecmp, Vv Cảm ơn bạn. tin nhắn được chỉnh sửa.
kyb

TODO: sử dụng cout << boolalphachứ không phải của tôi bool2strvì Nó để ngầm chuyển đổi bool thành ký tự cho luồng.
kyb

Nó nằm trong <String.h> trong các thư viện của gcc.

7

Làm điều này mà không sử dụng Boost có thể được thực hiện bằng cách lấy con trỏ chuỗi C c_str()và sử dụng strcasecmp:

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

6

Giả sử bạn đang tìm kiếm một phương pháp và không phải là một chức năng ma thuật đã tồn tại, thực sự không có cách nào tốt hơn. Tất cả chúng ta đều có thể viết các đoạn mã với các thủ thuật thông minh cho các bộ ký tự giới hạn, nhưng vào cuối ngày, vào lúc nào đó, bạn phải chuyển đổi các ký tự.

Cách tiếp cận tốt nhất cho việc chuyển đổi này là làm như vậy trước khi so sánh. Điều này cho phép bạn rất linh hoạt khi nói đến các sơ đồ mã hóa, mà toán tử so sánh thực tế của bạn nên không biết gì.

Tất nhiên bạn có thể 'ẩn' chuyển đổi này đằng sau hàm hoặc lớp chuỗi của riêng bạn, nhưng bạn vẫn cần chuyển đổi các chuỗi trước khi so sánh.


6

Tôi đã viết một phiên bản char_traits không phân biệt chữ hoa chữ thường để sử dụng với std :: basic_ chuỗi để tạo chuỗi std :: không phân biệt chữ hoa chữ thường khi thực hiện so sánh, tìm kiếm, v.v. bằng cách sử dụng các hàm thành viên std :: basic_ chuỗi tích hợp.

Nói cách khác, tôi muốn làm một cái gì đó như thế này.

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

assert( a == b );

... mà std :: chuỗi không thể xử lý. Đây là cách sử dụng char_traits mới của tôi:

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

assert( a == b );

... và đây là việc thực hiện:

/*  ---

        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
Điều này hoạt động đối với các ký tự thông thường, nhưng sẽ không hoạt động đối với tất cả Unicode, vì việc thu hút không nhất thiết là hai chiều (có một ví dụ điển hình trong tiếng Hy Lạp liên quan đến sigma mà tôi không thể nhớ ngay bây giờ; và bạn cũng không thể có được sự so sánh phù hợp)
coppro

1
Đó thực sự là cách sai lầm để đi về nó. Độ nhạy trường hợp không nên là một thuộc tính của chính chuỗi. Điều gì xảy ra khi cùng một đối tượng chuỗi cần cả so sánh phân biệt chữ hoa chữ thường và chữ hoa chữ thường?
Ferruccio

Nếu phân biệt chữ hoa chữ thường không thích hợp là "một phần" của chuỗi, thì cả hàm find () đều không có. Điều đó, đối với bạn, có thể là sự thật, và điều đó tốt. IMO điều tuyệt vời nhất về C ++ là nó không tạo ra một mô hình cụ thể nào đối với người lập trình. Đó là những gì bạn muốn / cần nó để được.
John Dibling

Trên thực tế, tôi nghĩ rằng hầu hết C ++ - guru (như những người trong ủy ban tiêu chuẩn) đều đồng ý rằng đó là một sai lầm khi đặt find () trong std :: basic_opes <> cùng với rất nhiều thứ khác cũng có thể được đặt vào chức năng miễn phí. Bên cạnh đó là một số vấn đề với việc đưa nó vào loại.
Andreas Magnusson

Như những người khác đã chỉ ra, có hai điều sai chính với giải pháp này (trớ trêu thay, một là giao diện và hai là giao diện ;-)).
Konrad Rudolph

4

Tôi đã có kinh nghiệm sử dụng các Thành phần Quốc tế cho các thư viện Unicode - chúng cực kỳ mạnh mẽ và cung cấp các phương thức để chuyển đổi, hỗ trợ ngôn ngữ, kết xuất ngày và giờ, ánh xạ trường hợp (mà bạn dường như không muốn) và đối chiếu , trong đó bao gồm so sánh không phân biệt chữ hoa và chữ thường (và hơn thế nữa). Tôi chỉ sử dụng phiên bản C ++ của các thư viện, nhưng dường như chúng cũng có phiên bản Java.

Các phương thức tồn tại để thực hiện so sánh chuẩn hóa như được đề cập bởi @Coincoin và thậm chí có thể tính đến ngôn ngữ - ví dụ (và đây là một ví dụ sắp xếp, không hoàn toàn bình đẳng), theo truyền thống ở Tây Ban Nha (ở Tây Ban Nha), kết hợp chữ cái "sẽ" sắp xếp giữa "l" và "m", vì vậy "lz" <"ll" <"ma".


4

Chỉ sử dụng strcmp()cho trường hợp nhạy cảm và strcmpi()hoặc stricmp()cho trường hợp so sánh không nhạy cảm. Cả hai đều nằm trong tệp tiêu đề<string.h>

định dạng:

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

Sử dụng:

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;

Đầu ra

táo và ApPlE giống nhau

a đến trước b, vì vậy táo đến trước bóng


2
Downvote vì đây không phải là cách làm của C ++.
Thomas Daugaard

Đây là quy ước c ++ tại trường đại học của tôi nhưng tôi sẽ ghi nhớ khi đăng ở đây
reubenjohn

4
stricmp là một phần mở rộng của Microsoft AFAIK. BSD dường như có strcasecmp () thay vào đó.
uliwitness

3

Đến bữa tiệc muộn, nhưng đây là một biến thể sử dụng std::locale, và do đó xử lý chính xác tiếng Thổ Nhĩ Kỳ:

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

cung cấp cho bạn một functor sử dụng ngôn ngữ hoạt động để chuyển đổi các ký tự thành chữ thường, sau đó bạn có thể sử dụng thông qua std::transformđể tạo các chuỗi chữ thường:

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

Điều này cũng hoạt động cho các wchar_tchuỗi dựa.


2

Chỉ cần một lưu ý về bất kỳ phương pháp nào cuối cùng bạn chọn, nếu phương pháp đó xảy ra bao gồm việc sử dụng strcmp một số câu trả lời gợi ý:

strcmpkhông hoạt động với dữ liệu Unicode nói chung. Nói chung, nó thậm chí không hoạt động với các bảng mã Unicode dựa trên byte, chẳng hạn như utf-8, vì strcmpchỉ thực hiện so sánh byte trên mỗi byte và các điểm mã Unicode được mã hóa trong utf-8 có thể mất hơn 1 byte. Trường hợp Unicode cụ thể duy nhất strcmpxử lý đúng là khi một chuỗi được mã hóa bằng mã hóa dựa trên byte chỉ chứa các điểm mã dưới U + 00FF - thì so sánh byte trên mỗi byte là đủ.


2

Ngay từ đầu năm 2013, dự án ICU, được IBM duy trì, là một câu trả lời khá hay cho vấn đề này.

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

ICU là một "thư viện Unicode hoàn chỉnh, di động theo dõi chặt chẽ các tiêu chuẩn ngành." Đối với vấn đề cụ thể của so sánh chuỗi, đối tượng Collation thực hiện những gì bạn muốn.

Dự án Mozilla đã thông qua ICU để quốc tế hóa trong Firefox vào giữa năm 2012; bạn có thể theo dõi các cuộc thảo luận kỹ thuật, bao gồm các vấn đề về hệ thống xây dựng và kích thước tệp dữ liệu, tại đây:


2

Có vẻ như các giải pháp trên không sử dụng phương pháp so sánh và thực hiện lại toàn bộ vì vậy đây là giải pháp của tôi và hy vọng nó có hiệu quả với bạn (Nó hoạt động tốt).

#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

Nếu bạn không muốn sử dụng thư viện Boost thì đây là giải pháp cho nó chỉ sử dụng tiêu đề io tiêu chuẩn C ++.

#include <iostream>

struct iequal
{
    bool operator()(int c1, int c2) const
    {
        // case insensitive comparison of two characters.
        return std::toupper(c1) == std::toupper(c2);
    }
};

bool iequals(const std::string& str1, const std::string& str2)
{
    // use std::equal() to compare range of characters using the functor above.
    return std::equal(str1.begin(), str1.end(), str2.begin(), iequal());
}

int main(void)
{
    std::string str_1 = "HELLO";
    std::string str_2 = "hello";

    if(iequals(str_1,str_2))
    {
        std::cout<<"String are equal"<<std::endl;   
    }

    else
    {
        std::cout<<"String are not equal"<<std::endl;
    }


    return 0;
}

Tôi tin rằng std :: toupper nằm trong #include <cctype>, bạn có thể cần phải bao gồm nó.
David Ledger

Nếu bạn sẽ sử dụng phiên bản toàn cầu như thế này :: toupper thì bạn có thể không cần bao gồm <ctype> vì có hai phiên bản c phiên bản và phiên bản c ++ với ngôn ngữ tôi đoán. Vì vậy, tốt hơn để sử dụng phiên bản toàn cầu ":: toupper ()"
HaSeeB MiR

giải pháp này thất bại khi một trong các chuỗi trống: "" - nó trả về true trong trường hợp đó khi nó trả về false
ekkis

0

Nếu bạn phải so sánh một chuỗi nguồn thường xuyên hơn với các chuỗi khác, một giải pháp tao nhã là sử dụng 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);

Đã thử lỗi này nhưng biên dịch lỗi: error: conversion from 'const char [5]' to non-scalar type 'std::wstring {aka std::basic_string<wchar_t>}' requested
De Khánh

ý xấu. Đó là giải pháp tồi tệ nhất.
Behrouz.M

Đây không phải là một giải pháp tốt, nhưng ngay cả khi bạn muốn sử dụng nó, bạn cần có chữ L trước các hằng số rộng nhất của mình, ví dụ L "TEST"
celticminstrel

Sẽ tốt đẹp nếu ai đó có thể giải thích tại sao nó là giải pháp tồi tệ nhất. Vì vấn đề hiệu suất? Tạo regex rất tốn kém, nhưng sau đó việc so sánh sẽ thực sự nhanh chóng.
smibe

nó có thể sử dụng và di động, vấn đề chính là trước tiên không thể chứa bất kỳ ký tự nào mà regex sử dụng. Nó không thể được sử dụng như một chuỗi so sánh chung vì điều đó. Nó cũng sẽ chậm hơn, có một lá cờ để làm cho nó hoạt động theo cách mà smibe nói nhưng vẫn không thể được sử dụng như một chức năng chung.
Bến

0

Một cách đơn giản để so sánh hai chuỗi trong c ++ (đã thử nghiệm cho windows) là sử dụng _stricmp

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

Nếu bạn đang tìm cách sử dụng với std :: string, một ví dụ:

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

Để biết thêm thông tin ở đây: https://msdn.microsoft.com/it-it/l Library / e0z9k731.aspx


Thật đáng để đọc stackoverflow.com/a/12414441/95309 ngoài câu trả lời này, vì đây là một) chức năng C và b) được cho là không khả dụng.
Claus Jørgensen

những gì chúng ta cần để làm cho công việc này?
ekkis

1
@ekkis để sử dụng _stricmp, bạn phải bao gồm <string.h> như bạn có thể đọc ở đây: docs.microsoft.com/en-us/cpp/c-r nb
l Library / report / Khăn

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

điều này có thể được thực hiện hiệu quả hơn nhiều, nhưng đây là một phiên bản cồng kềnh với tất cả các bit của nó.

không phải tất cả đều có thể mang theo, nhưng hoạt động tốt với bất cứ thứ gì có trên máy tính của tôi (không có ý tưởng, tôi là hình ảnh không phải từ ngữ)


Đây không phải là hỗ trợ Unicode, đó là những gì câu hỏi được hỏi.
Behrouz.M

Điều này không hỗ trợ các bộ ký tự không phải tiếng Anh.
Robert Andrzejuk

-3

Một cách dễ dàng để so sánh các chuỗi chỉ khác nhau bằng các ký tự viết thường và viết hoa là thực hiện so sánh ascii. Tất cả chữ in hoa và chữ thường khác nhau 32 bit trong bảng ascii, sử dụng thông tin này, chúng tôi có ...

    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
Theo đó, "++ j" sẽ được tìm thấy bằng "KKJ" và "1234" sẽ được tìm thấy bằng "QRST". Tôi nghi ngờ đó là điều mà bất cứ ai cũng muốn.
celticminstrel
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.