การเปรียบเทียบสตริงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ใน C ++ [ปิด]


373

วิธีที่ดีที่สุดในการทำการเปรียบเทียบสตริงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ใน C ++ โดยไม่ต้องแปลงสตริงเป็นตัวพิมพ์ใหญ่หรือตัวพิมพ์เล็กทั้งหมดคืออะไร

โปรดระบุว่าวิธีนี้เป็นมิตรกับ Unicode หรือไม่และสามารถพกพาได้อย่างไร


@ [Adam] (# 11679): แม้ว่าตัวแปรนี้ดีในแง่ของการใช้งาน แต่มันก็ไม่ดีในแง่ของประสิทธิภาพเพราะมันสร้างสำเนาที่ไม่จำเป็น ผมอาจจะมองข้ามบางสิ่งบางอย่าง แต่ผมเชื่อว่าสิ่งที่ดีที่สุด (ที่ไม่ใช่ Unicode) std::stricmpวิธีคือการใช้งาน มิฉะนั้นอ่านสิ่งที่สมุนไพรที่มีการพูด
Konrad Rudolph

ใน c หนึ่งมักจะถูกบังคับให้ toupper สายทั้งหมดแล้วเปรียบเทียบวิธี - หรือม้วนการเปรียบเทียบของคุณเอง: P
Michael Dorgan

คำถามต่อมามีคำตอบที่ง่ายกว่า: strcasecmp (อย่างน้อยสำหรับ BSD & POSIX คอมไพเลอร์) stackoverflow.com/questions/9182912/…
Móż

@ Mσᶎคำถามนี้ยังมีคำตอบนั้นด้วยข้อแม้สำคัญที่strcasecmpไม่ได้เป็นส่วนหนึ่งของมาตรฐานและหายไปจากคอมไพเลอร์ทั่วไปอย่างน้อยหนึ่งรายการ
Mark Ransom

คำตอบ:


317

Boost รวมถึงอัลกอริทึมที่มีประโยชน์สำหรับสิ่งนี้:

#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 หรือไม่ ผมคิดว่าไม่.
vladr

18
ไม่เพราะ UTF-8 อนุญาตให้เข้ารหัสสตริงที่เหมือนกันด้วยรหัสไบนารี่ที่แตกต่างกันเนื่องจากการเน้น, การรวม, ปัญหา bidi ฯลฯ
vy32

10
@ vy32 ไม่ถูกต้องอย่างแน่นอน! การรวมกันของ UTF-8 นั้นไม่เหมือนกัน มันจะต้องใช้การเป็นตัวแทนที่สั้นที่สุดเท่าที่จะทำได้หากไม่เป็นลำดับ UTF-8 ที่ผิดรูปแบบหรือจุดรหัสที่ต้องได้รับการดูแลอย่างดี
วิซ

48
@Wiz คุณไม่สนใจปัญหาของการทำให้สตริง Unicode เป็นมาตรฐาน ñสามารถแทนด้วยการรวม ˜ ตามด้วย n หรือด้วยอักขระñ คุณต้องใช้การทำให้เป็นสตริงของการทำให้เป็นมาตรฐาน Unicode ก่อนที่จะทำการเปรียบเทียบ โปรดตรวจสอบรายงานทางเทคนิค Unicode # 15, unicode.org/reports/tr15
vy32

12
@wonkorealtime: เพราะ "ß" ถูกแปลงเป็นตัวพิมพ์ใหญ่คือ "SS": fileformat.info/info/unicode/char/df/index.htm
Mooing Duck

118

char_traitsใช้ประโยชน์จากมาตรฐาน จำได้ว่าstd::stringในความเป็นจริง typedef สำหรับหรือมากกว่าอย่างชัดเจนstd::basic_string<char> ประเภทอธิบายวิธีอักขระเปรียบเทียบวิธีที่พวกเขาคัดลอกวิธีที่พวกเขาโยน ฯลฯ ทั้งหมดที่คุณต้องทำคือการ typedef สตริงใหม่กว่าและให้กับคุณเองว่าเปรียบเทียบกรณี insensitivelystd::basic_string<char, std::char_traits<char> >char_traitsbasic_stringchar_traits

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;

โดยมีรายละเอียดเกี่ยวกับคุรุของจำนวนสัปดาห์ที่ 29


10
เท่าที่ฉันทราบจากการทดลองของฉันเองสิ่งนี้ทำให้ประเภทสตริงใหม่ของคุณไม่สามารถทำงานร่วมกับ std :: string ได้
Zan Lynx

8
แน่นอนว่ามันทำ - เพื่อประโยชน์ของตัวเอง สตริงกรณีตายเป็นอย่างอื่น: ไม่typedef std::basic_string<char, ci_char_traits<char> > istring typedef std::basic_string<char, std::char_traits<char> > string
Andreas Spindler

232
"สิ่งที่คุณต้องทำ ... "
ทิม MB

3
@ นาธานอาจใช้คอมไพเลอร์ที่สามารถดำเนินการ CSE ขั้นพื้นฐานบนรหัส ...
The Paramagnetic Croissant

17
การสร้างภาษาใด ๆ ที่บังคับให้เกิดความวิกลจริตเช่นนี้ในกรณีเล็ก ๆ น้อย ๆ นี้ควรและสามารถยกเลิกได้โดยไม่ต้องเสียใจ
Erik Aronesty

86

ปัญหาในการเพิ่มคือคุณต้องเชื่อมโยงและขึ้นอยู่กับการเพิ่ม ไม่ใช่เรื่องง่ายในบางกรณี (เช่น Android)

และการใช้ char_traits หมายถึงการเปรียบเทียบทั้งหมดของคุณนั้นไม่ตรงตามตัวพิมพ์ใหญ่ - เล็กซึ่งไม่ใช่สิ่งที่คุณต้องการ

สิ่งนี้น่าจะพอเพียง มันควรจะมีประสิทธิภาพพอสมควร ไม่ได้จัดการยูนิโค้ดหรืออะไรก็ตาม

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

27
ที่จริงแล้วห้องสมุดเพิ่มสตริงเป็นส่วนหัวเท่านั้นห้องสมุดจึงไม่จำเป็นต้องเชื่อมโยงไปยังอะไร นอกจากนี้คุณสามารถใช้ยูทิลิตี้ 'เพิ่ม bcp' เพื่อคัดลอกเฉพาะส่วนหัวของสตริงไปยังทรีต้นทางของคุณดังนั้นคุณไม่จำเป็นต้องใช้ไลบรารีเพิ่มเต็ม
เกร็ต

อาฉันไม่รู้เกี่ยวกับ bcp มันดูมีประโยชน์จริงๆ ขอบคุณสำหรับข้อมูล!
Timmmm

9
เป็นการดีที่จะทราบว่าเป็นเวอร์ชันที่ง่ายและไม่เพิ่มการพึ่งพา
Deqing

2
@Anna ไลบรารีข้อความของการเพิ่มจะต้องมีการสร้างและเชื่อมโยง ใช้ IBM ICU
Behrouz.M

มีให้ใช้กับ C ++ 11
Martian

58

ถ้าคุณอยู่ในระบบ POSIX คุณสามารถใช้strcasecmp ฟังก์ชันนี้ไม่ได้เป็นส่วนหนึ่งของมาตรฐาน C แม้ว่าจะไม่มีอยู่ใน Windows สิ่งนี้จะทำการเปรียบเทียบแบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่บนตัวอักษรแบบ 8 บิตตราบใดที่โลแคลเป็น POSIX หากโลแคลไม่ใช่ POSIX ผลลัพธ์จะไม่ถูกกำหนด (ดังนั้นจึงอาจทำการเปรียบเทียบแบบโลคัลไลซ์หรืออาจไม่ได้) ไม่สามารถใช้อักขระตัวกว้างได้

ความล้มเหลวนั้นการใช้งานไลบรารี C ในอดีตจำนวนมากมีฟังก์ชัน stricmp () และ strnicmp () Visual C ++ บน Windows เปลี่ยนชื่อสิ่งเหล่านี้โดยนำหน้าด้วยเครื่องหมายขีดล่างเพราะพวกเขาไม่ได้เป็นส่วนหนึ่งของมาตรฐาน ANSI ดังนั้นในระบบที่เรียกว่า_stricmp หรือ _strnicmp_strnicmp ห้องสมุดบางแห่งอาจมีฟังก์ชั่นเทียบเท่าตัวอักษรกว้างหรือหลายไบต์ (โดยทั่วไปจะมีชื่อเช่น wcsicmp, mbcsicmp และอื่น ๆ )

C และ C ++ นั้นต่างก็ไม่รู้ปัญหาสากลมากนักดังนั้นจึงไม่มีทางออกที่ดีสำหรับปัญหานี้ยกเว้นการใช้ห้องสมุดบุคคลที่สาม ตรวจสอบIBM ICU (International Components for Unicode)หากคุณต้องการไลบรารี่ที่แข็งแกร่งสำหรับ C / C ++ ICU สำหรับทั้งระบบ Windows และ Unix


53

คุณกำลังพูดถึงการเปรียบเทียบแบบตัวพิมพ์เล็กหรือตัวพิมพ์ใหญ่เปรียบเทียบ Unicode

การเปรียบเทียบแบบใบ้จะไม่พบสตริงที่อาจเหมือนกัน แต่ไม่เท่ากับไบนารี

ตัวอย่าง:

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

ทุกคนมีความเท่าเทียมกัน แต่พวกเขายังมีการเป็นตัวแทนไบนารีที่แตกต่างกัน

ที่กล่าวว่าUnicode Normalizationควรเป็นข้อบังคับโดยเฉพาะอย่างยิ่งถ้าคุณวางแผนที่จะสนับสนุนภาษาอังกูล, Thaïและภาษาเอเชียอื่น ๆ

นอกจากนี้ IBM อัลกอริทึม Unicode ที่ได้รับการปรับปรุงให้ดีที่สุดยังเป็นสิทธิบัตรและทำให้พวกเขาเปิดเผยต่อสาธารณะ พวกเขายังคงดำเนินการ: IBM ICU


2
คุณอาจต้องการแก้ไขการเชื่อมโยงไปยังsite.icu-project.org ที่ ICU
DevSolar

31

boost :: iequals ไม่รองรับ utf-8 ในกรณีของ string คุณสามารถใช้เพิ่ม :: สถานที่เกิดเหตุ

comparator<char,collator_base::secondary> cmpr;
cout << (cmpr(str1, str2) ? "str1 < str2" : "str1 >= str2") << endl;
  • หลัก - ละเว้นการเน้นเสียงและตัวอักษรพิมพ์เปรียบเทียบตัวอักษรฐานเท่านั้น ตัวอย่างเช่น "facade" และ "Façade" เหมือนกัน
  • มัธยมศึกษา - ละเว้นกรณีตัวอักษร แต่ให้คำนึงถึงสำเนียง "facade" และ "façade" นั้นแตกต่างกัน แต่ "Façade" และ "façade" นั้นเหมือนกัน
  • ตติยภูมิ - พิจารณาทั้งกรณีและสำเนียง: "Façade" และ "façade" นั้นแตกต่างกัน ละเว้นเครื่องหมายวรรคตอน
  • Quaternary - พิจารณาตัวพิมพ์เล็กและตัวใหญ่ทั้งหมด คำจะต้องเหมือนกันในแง่ของการเป็นตัวแทน Unicode
  • เหมือนกัน - เหมือน quaternary แต่เปรียบเทียบจุดโค้ดด้วย

30

ความคิดแรกของฉันสำหรับรุ่นที่ไม่ใช่ Unicode คือการทำสิ่งนี้:


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

คุณสามารถใช้strcasecmpกับ Unix หรือstricmpบน Windows

สิ่งหนึ่งที่ยังไม่ได้รับการกล่าวถึงคือถ้าคุณใช้สตริง stl กับวิธีการเหล่านี้คุณควรเปรียบเทียบความยาวของสองสตริงก่อนเนื่องจากข้อมูลนี้มีให้คุณในคลาสสตริงแล้ว สิ่งนี้สามารถป้องกันไม่ให้ทำการเปรียบเทียบสตริงที่มีราคาแพงหากทั้งสองสตริงที่คุณกำลังเปรียบเทียบไม่ได้มีความยาวเท่ากันในตอนแรก


เนื่องจากการพิจารณาความยาวของสตริงประกอบด้วยการวนซ้ำอักขระทุกตัวในสตริงและเปรียบเทียบกับ 0 จึงมีความแตกต่างกันมากระหว่างนั้นและเพิ่งเปรียบเทียบสตริงทันทีหรือไม่ ฉันเดาว่าคุณจะได้ตำแหน่งของหน่วยความจำที่ดีขึ้นในกรณีที่ทั้งสองสายไม่ตรงกัน แต่อาจเป็นเกือบ 2x runtime ในกรณีที่มีการแข่งขัน
uliwitness

3
C ++ 11 ระบุว่าความซับซ้อนของ std :: string :: length ต้องคงที่: cplusplus.com/reference/string/string/length
bradtgmurray

1
นั่นเป็นเรื่องสนุกจริง แต่มีผลเล็กน้อยที่นี่ strcasecmp () และ stricmp () ทั้งคู่ใช้สตริง C ที่ไม่ได้ตกแต่งดังนั้นจึงไม่มี std :: string ที่เกี่ยวข้อง
uliwitness

3
วิธีการเหล่านี้จะส่งกลับ -1 ถ้าคุณเปรียบเทียบ "a" vs "ab" ความยาวต่างกัน แต่ "a" มาก่อน "ab" ดังนั้นการเปรียบเทียบความยาวจึงไม่สามารถทำได้หากผู้โทรสนใจเกี่ยวกับการสั่งซื้อ
นาธาน

14

ฟังก์ชันสตริง Visual C ++ ที่สนับสนุนยูนิโค้ด: http://msdn.microsoft.com/en-us/library/cc194799.aspx

สิ่งที่คุณกำลังมองหาคือ _wcsnicmp


7
แดกดัน, "รหัสตัวอักษรกว้าง" ของ Microsoft ไม่ได้เป็น Unicode ที่สะอาดเพราะพวกเขาไม่ได้จัดการกับการปรับสภาพแบบ Unicode
vy32

13

ฉันพยายามที่จะ cobble คำตอบที่ดีจากทุกโพสต์ดังนั้นช่วยฉันแก้ไข:

นี่คือวิธีการทำเช่นนี้แม้ว่ามันจะแปลงสตริงและไม่เป็นมิตรกับ Unicode แต่ก็ควรพกพาได้ซึ่งเป็นข้อดี:

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

จากสิ่งที่ฉันได้อ่านนี้เป็นแบบพกพามากกว่า stricmp () เพราะ stricmp () ไม่ได้เป็นส่วนหนึ่งของ std ไลบรารี่ แต่ดำเนินการโดยผู้ขายคอมไพเลอร์ส่วนใหญ่เท่านั้น

เพื่อให้เกิดการใช้งานที่เป็นมิตรกับ Unicode อย่างแท้จริงคุณต้องออกไปนอกห้องสมุด std หนึ่งไลบรารีของบุคคลที่สามที่ดีคือIBM ICU (International Components for Unicode)

นอกจากนี้ยังเพิ่ม :: iequalsยังมีสาธารณูปโภคที่ดีพอสมควรสำหรับการทำเรียงลำดับของการเปรียบเทียบนี้


คุณช่วยบอกได้ไหมว่า :: tolower หมายถึงอะไรทำไมคุณถึงใช้ tolower แทน tolower () และ '::' ก่อนหน้านี้คืออะไร? ขอบคุณ
VextoR

17
นี่ไม่ใช่วิธีที่มีประสิทธิภาพมากคุณทำสำเนาของทั้งสองสายและแปลงทั้งหมดแม้ว่าตัวอักษรตัวแรกจะแตกต่างกัน
Timmmm

2
หากคุณกำลังจะทำสำเนาอยู่ทำไมไม่ส่งผ่านค่าแทนโดยอ้างอิง
celticminstrel

ฉันคิดว่ามันเป็นเคล็ดลับง่ายๆโดยไม่เพิ่ม :)
cmcromance

1
คำถามที่ถามอย่างชัดเจนไม่ได้transformสตริงทั้งหมดก่อนที่จะทำการเปรียบเทียบ
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);})

คุณสามารถใช้รหัสข้างต้นใน C ++ 14 หากคุณไม่ได้อยู่ในตำแหน่งที่จะใช้การเพิ่ม คุณต้องใช้std::towlowerสำหรับตัวอักษรขนาดใหญ่


4
ฉันคิดว่าคุณต้องเพิ่ม a str1.size() == str2.size() &&ข้างหน้าเพื่อที่จะไม่ออกนอกขอบเขตเมื่อ str2 เป็นคำนำหน้าของ str1
uroeuroburɳ

11

Boost.Stringห้องสมุดมีจำนวนมากของอัลกอริทึมสำหรับการทำเปรียบเทียบกรณี insenstive และอื่น ๆ

คุณสามารถใช้งานของคุณเอง แต่ทำไมต้องกังวลเมื่อมันถูกทำไปแล้ว?


1
ไม่มีวิธีในตัวกับ std :: string?
WilliamKF

6
ไม่ไม่มี
Dean Harding

3
"... ทำไมต้องรำคาญใจเมื่อทำไปแล้ว?" - จะทำอย่างไรถ้าคุณไม่ได้ใช้ Boost OP ไม่มีแท็กพร้อมกับคำถาม
jww

11

FYI strcmp()และstricmp()มีความเสี่ยงที่จะบัฟเฟอร์ล้นเนื่องจากพวกเขาเพียงแค่ดำเนินการจนกว่าพวกเขาจะตีสิ้นสุดเทอร์มินัล มันปลอดภัยต่อการใช้งานและ_strncmp()_strnicmp()


6
True แม้ว่า overREADing buffer จะมีอันตรายน้อยกว่า overWRITEing buffer
Adam Rosenfield

4
stricmp()และstrnicmp()ไม่ได้เป็นส่วนหนึ่งของมาตรฐาน POSIX :-( แต่คุณสามารถค้นหาstrcasecmp(), strcasecmp_l(), strncasecmp()และstrncasecmp_l()ในส่วนหัวของ POSIX strings.h:-) ดูopengroup.org
olibre

2
@ AdamRosenfield 'แย่กว่า' ขึ้นอยู่กับบริบท ในการรักษาความปลอดภัยบางครั้งจุดรวมของการเขียนทับคือการได้รับมากเกินไป
karmakaze

10

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

การสาธิต


วิธีนี้อาจไม่ปลอดภัยและไม่พกพาได้ std::tolowerใช้งานได้เฉพาะเมื่ออักขระนั้นมีการเข้ารหัส ASCII ไม่มีการรับประกันดังกล่าวสำหรับstd::string- ดังนั้นจึงเป็นพฤติกรรมที่ไม่ได้กำหนดได้อย่างง่ายดาย
พลาสมาเซล

@plasmacel จากนั้นใช้ฟังก์ชั่นที่ใช้การเข้ารหัส w / อื่น ๆ
Brian Rodriguez

9

สำหรับการเปรียบเทียบสตริงที่ไม่ต้องคำนึงถึงขนาดตัวพิมพ์พื้นฐานของฉันฉันไม่ต้องการใช้ไลบรารี่ภายนอกและฉันไม่ต้องการคลาสสตริงแยกต่างหากที่มีลักษณะตัวพิมพ์เล็กและตัวพิมพ์เล็กที่ไม่เข้ากันกับสตริงอื่น ๆ ทั้งหมดของฉัน

ดังนั้นสิ่งที่ฉันคิดไว้คือ:

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

ฟังก์ชั่นที่เรียบง่ายพร้อมโอเวอร์โหลดหนึ่งตัวสำหรับถ่านและอีกฟังก์ชั่นสำหรับ whar_t ไม่ใช้สิ่งที่ไม่ได้มาตรฐานดังนั้นควรปรับใช้กับแพลตฟอร์มใด ๆ

การเปรียบเทียบความเท่าเทียมกันจะไม่พิจารณาปัญหาต่างๆเช่นการเข้ารหัสความยาวผันแปรและการทำให้เป็นมาตรฐาน Unicode แต่ basic_string ไม่สนับสนุนสิ่งที่ฉันรู้อยู่แล้วและปกติแล้วมันก็ไม่เป็นปัญหา

ในกรณีที่จำเป็นต้องใช้การจัดการข้อความพจนานุกรมที่ซับซ้อนยิ่งขึ้นคุณต้องใช้ไลบรารี่ของบุคคลที่สามอย่างบูสต์ซึ่งเป็นสิ่งที่คาดหวัง


2
คุณอาจทำให้ฟังก์ชั่นหนึ่งนั้นถ้าคุณทำให้มันเป็นแม่แบบและใช้ basic_string <T> แทนที่จะแยกรุ่นสตริง / wstring?
uliwitness

2
เท็มเพลตฟังก์ชั่นเดียวจะเรียกใช้ toupper หรือ towupper ได้อย่างไรโดยไม่ต้องหันมาใช้ความเชี่ยวชาญหรือมาโครฟังก์ชันโอเวอร์โหลดดูเหมือนจะเป็นการใช้งานที่ง่ายและเหมาะสมกว่า
Neutrino

9

สั้นและดี ไม่มีการพึ่งพาอื่นใดนอกจากการขยาย std C lib

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

ผลตอบแทนจริงถ้าstr1และstr2เท่ากับ strcasecmpไม่อาจมีอยู่อาจจะมี analogs stricmp, strcmpiฯลฯ

รหัสตัวอย่าง:

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

เอาท์พุท:

true
true
true
true
true

6
มันเป็นเรื่องแปลกที่ C ++ มาตรฐาน :: สตริงมีวิธีการเปรียบเทียบไม่ละเลยกรณี ..
KYB

1
"strcasecmp ไม่ได้เป็นส่วนหนึ่งของมาตรฐาน" - Mark Ransom 1 ธ.ค. '14 เวลา 19:57 น.
Liviu

ใช่ แต่คอมไพเลอร์สมัยใหม่ส่วนใหญ่จะมีมันหรืออะนาล็อกที่มีชื่ออื่น stricmp, strcmpi, strcasecmpฯลฯ ขอบคุณ แก้ไขข้อความแล้ว
kyb

สิ่งที่ต้องทำ: ใช้cout << boolalphaมากกว่าของฉันbool2strเพราะมันจะแปลง bool เป็นตัวอักษรสำหรับการสตรีมโดยปริยาย
kyb

มันอยู่ใน <strings.h> ในไลบรารีของ gcc
Owl

7

การทำสิ่งนี้โดยไม่ใช้ Boost สามารถทำได้โดยการนำตัวชี้สตริง C มาc_str()ใช้ด้วยstrcasecmp:

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

6

สมมติว่าคุณกำลังมองหาวิธีการและไม่ใช่ฟังก์ชั่นเวทย์มนตร์ที่มีอยู่แล้วไม่มีทางที่ดีกว่า เราทุกคนสามารถเขียนตัวอย่างโค้ดด้วยเทคนิคที่ชาญฉลาดสำหรับชุดอักขระที่ จำกัด แต่ในตอนท้ายของวันในบางจุดคุณต้องแปลงอักขระ

วิธีที่ดีที่สุดสำหรับการแปลงนี้คือการทำก่อนการเปรียบเทียบ สิ่งนี้ช่วยให้คุณมีความยืดหยุ่นอย่างมากเมื่อพูดถึงการเข้ารหัสแบบแผนซึ่งผู้ดำเนินการเปรียบเทียบที่แท้จริงของคุณควรไม่รู้

แน่นอนคุณสามารถ 'ซ่อน' การแปลงนี้ไว้ข้างหลังฟังก์ชันสตริงหรือคลาสของคุณเอง แต่คุณยังต้องแปลงสตริงก่อนที่จะทำการเปรียบเทียบ


6

ฉันเขียน char_traits รุ่นเล็กและเล็กสำหรับใช้กับ std :: basic_string เพื่อสร้าง std :: string ที่ไม่ตรงตามตัวพิมพ์ใหญ่ - เล็กเมื่อทำการเปรียบเทียบค้นหาและอื่น ๆ โดยใช้ฟังก์ชันสมาชิก std :: basic_string

ดังนั้นในคำอื่น ๆ ฉันต้องการทำอะไรเช่นนี้

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

assert( a == b );

... ที่ std :: string ไม่สามารถจัดการได้ นี่คือการใช้ char_traits ใหม่ของฉัน:

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

assert( a == b );

... และนี่คือการดำเนินการ:

/*  ---

        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
ใช้งานได้กับตัวอักษรปกติ แต่ไม่สามารถใช้ได้กับ Unicode ทั้งหมดเนื่องจากการตั้งค่าไม่จำเป็นต้องเป็นแบบสองทิศทาง (มีตัวอย่างที่ดีในภาษากรีกที่เกี่ยวข้องกับ sigma ที่ฉันจำไม่ได้ในตอนนี้ และคุณไม่สามารถได้รับการเปรียบเทียบที่เหมาะสมทางใดทางหนึ่ง)
coppro

1
นั่นเป็นวิธีที่ผิดที่จะไปเกี่ยวกับมัน การพิจารณาตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ไม่ควรเป็นคุณสมบัติของสตริงเอง จะเกิดอะไรขึ้นเมื่อวัตถุสตริงเดียวกันต้องเปรียบเทียบทั้งตัวพิมพ์เล็กและตัวพิมพ์เล็ก
Ferruccio

หากตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ไม่เหมาะสมที่จะเป็น "ส่วนหนึ่งของ" สตริงแสดงว่าไม่มีฟังก์ชัน find () เลย ซึ่งสำหรับคุณอาจเป็นจริงและก็ไม่เป็นไร IMO สิ่งที่ยิ่งใหญ่ที่สุดเกี่ยวกับ C ++ คือมันไม่ได้บังคับกระบวนทัศน์เฉพาะของโปรแกรมเมอร์ มันคือสิ่งที่คุณต้องการ / จำเป็นต้องเป็น
John Dibling

อันที่จริงฉันคิดว่า C ++ ส่วนใหญ่ - กูรู (เหมือนคนที่อยู่ในคณะกรรมการมาตรฐาน) ยอมรับว่ามันเป็นความผิดพลาดที่จะหา find () ใน std :: basic_string <> พร้อมกับสิ่งอื่น ๆ อีกมากมาย ฟังก์ชั่นฟรี นอกจากนี้ยังมีปัญหาบางอย่างกับการวางไว้ในประเภท
Andreas Magnusson

ตามที่คนอื่น ๆ ชี้ให้เห็นมีสองสิ่งที่สำคัญผิดกับวิธีนี้ (แดกดันหนึ่งคืออินเตอร์เฟซและอื่น ๆ คือการใช้ ;-))
Konrad Rudolph

4

ฉันมีประสบการณ์ที่ดีในการใช้งานคอมโพเนนต์ระหว่างประเทศสำหรับไลบรารี Unicode - พวกเขามีประสิทธิภาพอย่างยิ่งและให้วิธีการสำหรับการแปลง, การสนับสนุนสถานที่, การแสดงวันที่และเวลา, การแมปเคส (ซึ่งคุณไม่ต้องการ) และการเปรียบเทียบ , ซึ่งรวมถึงการเปรียบเทียบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่และเล็ก (และอื่น ๆ ) ฉันใช้ไลบรารีรุ่น C ++ เท่านั้น แต่ดูเหมือนว่าจะมีเวอร์ชัน Java เช่นกัน

วิธีการมีอยู่เพื่อทำการเปรียบเทียบแบบปกติตามที่อ้างอิงโดย @Coincoin และยังสามารถอธิบายถึงสถานที่ - ตัวอย่างเช่น (และนี่เป็นตัวอย่างการเรียงลำดับไม่เท่าเทียมกันอย่างเคร่งครัด) ตามธรรมเนียมในภาษาสเปน (ในสเปน) การรวมตัวอักษร "l" และ "m" ดังนั้น "lz" <"ll" <"ma"


4

ใช้strcmp()สำหรับการพิจารณาตัวพิมพ์เล็กและตัวพิมพ์ใหญ่strcmpi()หรือstricmp()เพื่อการเปรียบเทียบแบบตัวพิมพ์เล็กและใหญ่ ซึ่งมีทั้งในไฟล์ส่วนหัว<string.h>

รูปแบบ:

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

การใช้งาน:

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;

เอาท์พุต

apple และ ApPlE เหมือนกัน

มาก่อน b ดังนั้นแอปเปิ้ลมาก่อนบอล


2
Downvote เพราะนี่เป็นวิธีการทำสิ่ง C + + แทบจะไม่
โทมัส Daugaard

นี่คือ C ++ ประชุมที่มหาวิทยาลัยของฉัน แต่ฉันจะเก็บไว้ในใจเมื่อโพสต์ที่นี่
reubenjohn

4
stricmp เป็น AFAIK ส่วนขยายของ Microsoft BSD ดูเหมือนจะมี strcasecmp () แทน
uliwitness

3

ไปงานปาร์ตี้สาย แต่ที่นี่เป็นรุ่นที่ใช้std::localeและจัดการอย่างถูกต้องตุรกี:

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

ให้ functor ที่ใช้โลแคลที่ใช้งานอยู่เพื่อแปลงอักขระเป็นตัวพิมพ์เล็กซึ่งคุณสามารถใช้ผ่านstd::transformเพื่อสร้างสตริงตัวพิมพ์เล็ก:

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

นอกจากนี้ยังใช้งานได้กับwchar_tสตริงตาม


2

เพียงทราบว่าวิธีการใดที่คุณเลือกในที่สุดหากวิธีการที่เกิดขึ้นรวมถึงการใช้ strcmpคำตอบที่แนะนำ:

strcmpไม่ทำงานกับข้อมูล Unicode โดยทั่วไป โดยทั่วไปแล้วมันไม่ได้ทำงานกับการเข้ารหัส Unicode ที่เป็นไบต์เช่น utf-8 เนื่องจากstrcmpทำการเปรียบเทียบไบต์ต่อไบต์เท่านั้นและการเข้ารหัส Unicode ใน utf-8 อาจใช้เวลามากกว่า 1 ไบต์ กรณี Unicode ที่เจาะจงเฉพาะstrcmpจัดการอย่างถูกต้องคือเมื่อสตริงที่เข้ารหัสด้วยการเข้ารหัสแบบไบต์ประกอบด้วยจุดรหัสเฉพาะด้านล่าง U + 00FF - ดังนั้นการเปรียบเทียบไบต์ต่อไบต์ก็เพียงพอแล้ว


2

เมื่อต้นปี 2556 โครงการ ICU ที่ IBM ดูแลไว้เป็นคำตอบที่ดีในเรื่องนี้

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

ICU เป็น "ไลบรารี Unicode แบบพกพาที่สมบูรณ์และสามารถติดตามมาตรฐานอุตสาหกรรมได้อย่างใกล้ชิด" สำหรับปัญหาเฉพาะของการเปรียบเทียบสตริงวัตถุการเรียงจะทำสิ่งที่คุณต้องการ

โครงการ Mozilla ใช้ ICU สำหรับการทำให้เป็นสากลใน Firefox ในกลางปี ​​2012 คุณสามารถติดตามการอภิปรายทางวิศวกรรมรวมถึงปัญหาของการสร้างระบบและขนาดไฟล์ข้อมูลได้ที่นี่:


2

ดูเหมือนว่าโซลูชันข้างต้นไม่ได้ใช้วิธีการเปรียบเทียบและนำไปใช้ทั้งหมดอีกครั้งดังนั้นนี่คือโซลูชันของฉันและหวังว่าจะได้ผลสำหรับคุณ (ใช้งานได้ดี)

#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แล้วนี่คือวิธีการแก้ปัญหาโดยใช้ส่วนหัว io มาตรฐาน 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;
}

ฉันเชื่อว่า std :: toupper อยู่ใน #include <cctype> คุณอาจต้องรวมมัน
David Ledger

หากคุณจะใช้เวอร์ชันทั่วโลกเช่นนี้ :: toupper คุณอาจไม่จำเป็นต้องรวม <ctype> เนื่องจากมีสองเวอร์ชันคือเวอร์ชัน c และเวอร์ชัน c ++ ที่มีโลแคลที่ฉันเดา ดังนั้นดีกว่าที่จะใช้รุ่นสากล ":: toupper ()"
HaSeeB MiR

วิธีการแก้ปัญหานี้ล้มเหลวเมื่อหนึ่งในสตริงว่างเปล่า: "" - มันจะกลับมาจริงในกรณีนั้นเมื่อมันควรจะกลับเท็จ
22419

0

หากคุณต้องเปรียบเทียบสตริงซอร์สบ่อยขึ้นกับสตริงอื่น ๆ โซลูชันที่สวยงามวิธีหนึ่งคือใช้ 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);

พยายามนี้ แต่มีข้อผิดพลาดในการคอมไพล์: error: conversion from 'const char [5]' to non-scalar type 'std::wstring {aka std::basic_string<wchar_t>}' requested
Deqing

ความคิดที่ไม่ดี มันเป็นทางออกที่เลวร้ายที่สุด
Behrouz.M

นี่ไม่ใช่ทางออกที่ดี แต่แม้ว่าคุณต้องการใช้คุณต้องมี L หน้าค่าคงที่ที่กว้างที่สุดของคุณเช่น L "TEST"
celticminstrel

จะดีถ้ามีคนอธิบายได้ว่าทำไมมันถึงเป็นทางออกที่แย่ที่สุด เพราะปัญหาเรื่องประสิทธิภาพ? การสร้าง regex นั้นมีราคาแพง แต่หลังจากนั้นการเปรียบเทียบควรเร็วมาก
smibe

มันใช้งานได้และพกพาได้ปัญหาสำคัญคืออันดับแรกไม่สามารถมีตัวอักษรใด ๆ ที่ regex ใช้ ไม่สามารถใช้เป็นสตริงทั่วไปเปรียบเทียบได้เนื่องจากสิ่งนั้น มันจะช้าลงด้วยมีธงที่จะทำให้มันทำงานในแบบที่คนพูด แต่ไม่สามารถใช้เป็นฟังก์ชั่นทั่วไปได้
Ben

0

วิธีง่ายๆในการเปรียบเทียบสองสตริงใน c ++ (ทดสอบสำหรับ windows) คือการใช้_stricmp

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

หากคุณต้องการใช้กับ std :: string ตัวอย่าง:

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

สำหรับข้อมูลเพิ่มเติมที่นี่: https://msdn.microsoft.com/it-it/library/e0z9k731.aspx


มันคุ้มค่าที่จะอ่านstackoverflow.com/a/12414441/95309เพิ่มเติมจากคำตอบนี้เนื่องจากเป็น) ฟังก์ชั่น C และ b) ไม่ควรพกพา
ซานตาคลอสJørgensen

#include เราต้องทำอะไรให้ทำงานนี้ได้บ้าง
ekkis

1
@ekkis ใช้ _stricmp คุณต้องรวม <สตริง> ที่คุณสามารถอ่านได้ที่นี่: 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;
        }
      }
}

นี่อาจจะทำให้มีประสิทธิภาพมากขึ้น แต่นี่เป็นรุ่นใหญ่ที่มีบิตทั้งหมดของมัน

ไม่ใช่ทุกพกพาที่ แต่ทำงานได้ดีกับสิ่งที่อยู่ในคอมพิวเตอร์ของฉัน (ไม่ทราบว่าฉันเป็นภาพไม่ใช่คำ)


นี่ไม่ใช่การสนับสนุน Unicode ซึ่งเป็นคำถามที่ถาม
Behrouz.M

สิ่งนี้ไม่รองรับชุดอักขระที่ไม่ใช่ภาษาอังกฤษ
Robert Andrzejuk

-3

วิธีง่ายๆในการเปรียบเทียบสตริงที่แตกต่างกันโดยตัวพิมพ์เล็กและตัวพิมพ์ใหญ่คือทำการเปรียบเทียบ ASCII ตัวพิมพ์ใหญ่และตัวพิมพ์เล็กแตกต่างกัน 32 บิตในตาราง ascii โดยใช้ข้อมูลนี้เรามีดังต่อไปนี้ ...

    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
ตามนี้ "++ j" จะเท่ากับ "KKJ" และ "1234" จะเท่ากับ "QRST" ฉันสงสัยว่าเป็นสิ่งที่ทุกคนต้องการ
celticminstrel
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.