C ++에서 대소 문자를 구분하지 않는 문자열 비교


373

문자열을 모두 대문자 또는 소문자로 변환하지 않고 C ++에서 대소 문자를 구분하지 않는 문자열 비교를 수행하는 가장 좋은 방법은 무엇입니까?

메소드가 유니 코드에 친숙한 지, 얼마나 이식성이 좋은지 알려주십시오.


@ [Adam] (# 11679) :이 변형은 유용성 측면에서 우수하지만 불필요한 사본을 생성하기 때문에 성능 측면에서 좋지 않습니다. 나는 무언가를 간과 할 수도 있지만 (유니 코드가 아닌) 가장 좋은 방법은을 사용하는 것 std::stricmp입니다. 그렇지 않으면 Herb 가 말한 내용을 읽으십시오 .
Konrad Rudolph

c에서, 일반적으로 전체 줄을 위로 올려 놓고 그런 식으로 비교하거나 자신의 비교를 굴리십시오 : 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 시퀀스 또는 코드 포인트입니다.
Wiz

48
@ 위즈에서는 유니 코드 문자열 정규화 문제를 무시하고 있습니다. ñ는 ~와 n, 또는 ñ 문자를 조합하여 표시 할 수 있습니다. 비교를 수행하기 전에 유니 코드 문자열 정규화를 사용해야합니다.
vy32

12
@wonkorealtime : 대문자로 변환 된 "ß"가 "SS"이기 때문에 : fileformat.info/info/unicode/char/df/index.htm
Mooing Duck

118

표준을 활용하십시오 char_traits. a std::string는 실제로에 대한 typedef std::basic_string<char>또는 더 명확하게 는 typedef입니다 std::basic_string<char, std::char_traits<char> >. 이 char_traits유형은 문자를 비교하는 방법, 복사 방법, 캐스트 방법 등을 설명합니다. 새 문자열을 typedef하고 대소 문자를 구분하지 않는 basic_string고유 한 사용자 정의 char_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
"당신이해야 할 일은 ..."
Tim MB

3
@Nathan은 아마도 코드에서 기본적인 CSE를 수행 할 수있는 컴파일러를 사용했을 것입니다.
The Paramagnetic Croissant

17
이 사소한 경우에 그러한 광기를 강요하는 언어 구성은 후회없이 버려 질 수 있어야합니다.
Erik Aronesty

86

부스트의 문제점은 부스트와 연결되어 있고 의존해야한다는 것입니다. 어떤 경우에는 쉽지 않습니다 (예 : 안드로이드).

그리고 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
실제로 부스트 문자열 라이브러리는 헤더 전용 라이브러리이므로 아무 것도 링크 할 필요가 없습니다. 또한 boost의 'bcp'유틸리티를 사용하여 문자열 헤더 만 소스 트리에 복사 할 수 있으므로 전체 boost 라이브러리가 필요하지 않습니다.
Gretchen

아, 나는 bcp에 대해 몰랐다. 그것은 정말로 유용하게 보인다. 정보에 대해서 감사드립니다!
Timmmm

9
간단하고 부스트 종속이 아닌 버전을 아는 것이 좋습니다.
Deqing

2
부스트 @Anna 텍스트 라이브러리를 구축하고 링크해야합니다. IBM ICU를 사용합니다.
Behrouz.M

C ++ 11과 함께 사용 가능
martian

58

POSIX 시스템 인 경우 strcasecmp 를 사용할 수 있습니다 . 이 기능은 표준 C의 일부가 아니며 Windows에서도 사용할 수 없습니다. 로케일이 POSIX 인 경우 8 비트 문자를 대소 문자를 구분하지 않고 비교합니다. 로케일이 POSIX가 아닌 경우 결과가 정의되지 않으므로 지역화 된 비교를 수행하거나 수행하지 않을 수 있습니다. 넓은 문자를 사용할 수 없습니다.

실패한 수많은 역사적인 C 라이브러리 구현에는 stricmp () 및 strnicmp () 함수가 있습니다. Windows의 Visual C ++는 ANSI 표준의 일부가 아니기 때문에 밑줄로 접두어를 붙여 이러한 이름을 모두 바꿨습니다 . 따라서이 시스템에서는 _stricmp 또는 _strnicmp 라고 합니다. 일부 라이브러리는 와이드 문자 또는 멀티 바이트 등가 기능 (일반적으로 이름이 wcsicmp, mbcsicmp 등)을 가질 수도 있습니다.

C 및 C ++는 모두 국제화 문제에 대해 거의 무지하므로 타사 라이브러리를 사용하는 것 외에는이 문제에 대한 좋은 해결책이 없습니다. 체크 아웃 IBM ICU (유니 코드에 대한 국제 구성 요소) 은 C / C에 대한 강력한 라이브러리를 필요로하는 경우 ++. ICU는 Windows 및 Unix 시스템 용입니다.


53

대소 문자를 구분하지 않는 비교 또는 정규화 된 유니 코드 비교에 대해 이야기하고 있습니까?

벙어리 비교는 같지만 이진 같지 않은 문자열을 찾지 않습니다.

예:

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

모두 동일하지만 서로 다른 이진 표현이 있습니다.

즉, 한글, 태국 및 기타 아시아 언어를 지원할 계획이라면 유니 코드 정규화 를 반드시 읽어야합니다.

또한 IBM은 특허를받은 대부분의 최적화 된 유니 코드 알고리즘을 공개하여 공개했습니다. 또한 구현을 유지 보수합니다. IBM ICU



31

boost :: iequals는 문자열의 경우 UTF-8과 호환되지 않습니다. boost :: locale을 사용할 수 있습니다 .

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"는 동일합니다.
  • 3 차-대소 문자와 악센트를 모두 고려하십시오. "Façade"와 "Fçade"는 다릅니다. 문장 부호를 무시하십시오.
  • 4 기-모든 경우, 악센트 및 문장 부호를 고려하십시오. 유니 코드 표현의 단어는 동일해야합니다.
  • 동일-사차와 동일하지만 코드 포인트도 비교합니다.

30

비 유니 코드 버전에 대한 나의 첫 생각은 다음과 같이하는 것이었다.


bool caseInsensitiveStringCompare(const string& str1, const string& str2) {
    if (str1.size() != str2.size()) {
        return false;
    }
    for (string::const_iterator c1 = str1.begin(), c2 = str2.begin(); c1 != str1.end(); ++c1, ++c2) {
        if (tolower(*c1) != tolower(*c2)) {
            return false;
        }
    }
    return true;
}

20

strcasecmpUnix 또는 stricmpWindows에서 사용할 수 있습니다 .

지금까지 언급되지 않은 한 가지 방법은 이러한 방법으로 stl 문자열을 사용하는 경우이 정보가 문자열 클래스에서 이미 사용 가능하므로 먼저 두 문자열의 길이를 비교하는 것이 유용하다는 것입니다. 이렇게하면 비교하는 두 문자열의 길이가 동일하지 않은 경우 비용이 많이 드는 문자열 비교를 수행하지 못할 수 있습니다.


문자열의 길이를 결정하는 것은 문자열의 모든 문자를 반복하고 0과 비교하는 것으로 구성되므로 문자열과 문자열을 비교하는 것 사이에는 실제로 많은 차이가 있습니까? 두 문자열이 일치하지 않는 경우 더 나은 메모리 지역성을 얻지 만 일치하는 경우 거의 2 배의 런타임이 될 것입니다.
uliwitness

3
C ++ 11은 std :: string :: length의 복잡성이 일정해야 함을 지정합니다. cplusplus.com/reference/string/string/length
bradtgmurray

1
그것은 재미있는 작은 사실이지만 여기에는 거의 관련이 없습니다. strcasecmp () 및 stricmp ()는 모두 데코레이션되지 않은 C 문자열을 사용하므로 std :: string과 관련이 없습니다.
uliwitness 2012

3
이 메소드는 "a"와 "ab"를 비교하면 -1을 리턴합니다. 길이는 다르지만 "a"는 "ab"앞에옵니다. 따라서 발신자가 주문에 관심이 있다면 단순히 길이를 비교하는 것은 불가능합니다.
Nathan


13

모든 게시물에서 좋은 답변을 함께 모 으려고 노력하고 있으므로 이것을 편집하도록 도와주세요.

문자열 변환을 수행하지만 유니 코드에 적합하지는 않지만 이식 가능한 방법은 다음과 같습니다.

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 ()는 실제로 std 라이브러리의 일부가 아니며 대부분의 컴파일러 공급 업체에서만 구현하기 때문에 stricmp ()보다 이식성이 뛰어납니다.

진정한 유니 코드 친화적 인 구현을 얻으려면 std 라이브러리 외부로 이동해야합니다. 좋은 타사 라이브러리 중 하나는 IBM ICU (International Components for Unicode)입니다.

또한 boost :: iequals 는 이러한 종류의 비교를 수행하는 데 매우 유용한 유틸리티를 제공합니다.


:: tolower의 의미, tolower () 대신 tolower를 사용할 수있는 이유 및 '::'이 무엇인지 알 수 있습니까? 감사합니다
VextoR

17
이것은 매우 효율적인 솔루션은 아닙니다. 첫 번째 문자가 다른 경우에도 두 문자열을 모두 복사하여 모두 변환합니다.
Timmmm

2
어쨌든 사본을 만들려면 참조 대신 값으로 전달하지 않겠습니까?
celticminstrel 2016 년

부스트가없는 간단한 팁이라고 생각합니다. :)
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
str1.size() == str2.size() &&str2가 str1의 접두사 일 때 경계를 벗어나지 않도록 앞에 를 추가해야한다고 생각합니다 .
ɲ ɳ '

11

Boost.String 라이브러리 등의 경우-insenstive 비교를하고위한 알고리즘을 많이했다.

당신은 당신 자신을 구현할 수 있지만, 이미 완료되었을 때 왜 귀찮게합니까?


1
std :: string에 내장 된 방법이 없습니까?
WilliamKF

6
아닙니다.
Dean Harding

3
"... 왜 이미 끝났을 때 귀찮게합니까?" -Boost를 사용하지 않으면 어떻게 되나요? OP에 질문이있는 태그가 없습니다.
jww

11

참고로, strcmp()그리고 stricmp()그들이 널 터미네이터를 칠 때까지 그냥 처리하기 때문에, 오버 플로우 버퍼에 취약하다. 그것은 사용하기 더 안전 _strncmp()하고 _strnicmp().


6
버퍼를 너무 많이 읽는 것이 버퍼를 너무 많이 쓰는 것보다 훨씬 덜 위험합니다.
아담 로젠 필드

4
stricmp()그리고 strnicmp():-( 그러나 당신이 찾을 수있는 POSIX 표준의 일부가 아닌 strcasecmp(), strcasecmp_l(), strncasecmp()strncasecmp_l()POSIX 헤더에 strings.h:-) 참조 opengroup.org을
olibre

2
@AdamRosenfield 'worse'는 상황에 따라 다릅니다. 보안 상, 덮어 쓰기의 요점은 덮어 쓰는 것입니다.
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

@plasmacel 그런 다음 다른 인코딩과 함께 작동하는 기능을 사용하십시오.
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); });
}

char에 대한 과부하와 whar_t에 대한 과부하를 가진 간단한 함수. 비표준을 사용하지 않으므로 모든 플랫폼에서 괜찮습니다.

평등 비교는 가변 길이 인코딩 및 유니 코드 정규화와 같은 문제를 고려하지 않지만 basic_string은 내가 아는 것을 지원하지 않으며 일반적으로 문제가되지 않습니다.

보다 복잡한 텍스트 사전 조작이 필요한 경우 Boost와 같은 타사 라이브러리를 사용하면됩니다.


2
템플릿으로 만들고 별도의 문자열 / wstring 버전 대신 basic_string <T>을 사용하면 해당 기능을 만들 수 있습니까?
uliwitness

2
단일 함수 템플릿이 특수화 또는 매크로를 사용하지 않고 어떻게 toupper 또는 towupper를 호출 할 수 있습니까?
Neutrino 2016 년

9

짧고 좋은. 확장 std C lib 이외의 다른 종속성은 없습니다 .

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

반환 진정한 경우 str1str2동일하다. strcasecmp존재하지 않을 수,있을 수 아날로그 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 ++ std :: string에 무시 대소 문자 비교 방법이없는 것이 이상합니다.
kyb

1
"strcasecmp는 표준의 일부가 아닙니다"-Mark Ransom 12 월 1 일 '14시 19:57
Liviu

예, 그러나 대부분의 최신 컴파일러에는 다른 컴파일러가 있습니다. stricmp, strcmpi, strcasecmp, 등 감사합니다. 메시지가 편집되었습니다.
kyb

TODO : 암시 적으로 bool을 스트림의 문자로 변환하기 때문에 cout << boolalpha내 대신 사용하십시오 bool2str.
kyb

gcc 라이브러리의 <strings.h>에 있습니다.
Owl

7

부스트를 사용하지 않고 이렇게는로 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

내장 std :: basic_string 멤버 함수를 사용하여 비교, 검색 등을 수행 할 때 대소 문자를 구분하지 않는 std :: string을 생성하기 위해 std :: basic_string과 함께 사용할 대소 문자를 구분하지 않는 char_traits 버전을 작성했습니다.

다시 말해, 나는 이런 식으로하고 싶었습니다.

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
이것은 정규 문자에서는 작동하지만 captitalization이 반드시 양방향 일 필요는 없으므로 모든 유니 코드에서 작동하지는 않습니다 (그리스어에는 시그마와 관련하여 현재 기억할 수없는 좋은 예가 있습니다. , 그리고 당신은 어느 쪽이든 적절한 비교를 얻을 수 없습니다)
coppro

1
정말 잘못된 길입니다. 대소 문자 구분은 문자열 자체의 속성이 아니어야합니다. 동일한 문자열 객체에 대 / 소문자를 구분하지 않고 대 / 소문자를 구분하지 않으면 어떻게됩니까?
Ferruccio

대소 문자 구분이 문자열의 "부분"에 적합하지 않은 경우 find () 함수가 아닙니다. 어느 쪽이든 당신에게 맞을 수도 있습니다. C ++의 가장 큰 점은 IMO가 프로그래머에게 특정 패러다임을 강요하지 않는다는 것입니다. 그것은 당신이 원하는 것입니다.
John Dibling

실제로, 나는 표준위원회의 것들과 같이 대부분의 C ++-guru가 std :: basic_string <>에 find ()를 똑같이 잘 배치 할 수있는 다른 많은 것들과 함께 넣는 것은 실수라고 생각합니다 무료 기능. 또한 유형에 넣는 데 몇 가지 문제가 있습니다.
Andreas Magnusson

다른 사람들이 지적 했듯이이 솔루션에는 두 가지 중요한 문제가 있습니다 (아이 론적으로는 인터페이스이고 다른 하나는 구현입니다 ;-)).
Konrad Rudolph

4

유니 코드 라이브러리 용 국제 구성 요소를 사용해 본 경험이 있습니다.이 라이브러리 는 매우 강력하며 변환, 로케일 지원, 날짜 및 시간 렌더링, 케이스 맵핑 (원하지 않는 것) 및 조합에 대한 메소드를 제공합니다. . 대소 문자를 구분하지 않고 대 / 소문자를 구분하지 않는 비교 등이 포함됩니다. 나는 C ++ 버전의 라이브러리 만 사용했지만 Java 버전도있는 것으로 보입니다.

@Coincoin이 참조하는 정규화 된 비교를 수행하는 방법이 있으며, 예를 들어 스페인어 (스페인)에서 문자 조합 "ll"은 다음과 같이 정렬됩니다 (예 : 엄격하게 같지 않은 정렬 예). "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는 동일합니다

a는 b보다 먼저 나오므로 사과는 공보다 먼저옵니다


2
이것은 C ++ 방식이 아니기 때문에 공감할 수 있습니다.
Thomas Daugaard

이것은 내 대학에서 C ++ 컨벤션이지만 여기에 게시 할 때 명심하십시오
reubenjohn

4
stricmp는 Microsoft 확장 AFAIK입니다. 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를 제공하며 via std::transform를 사용하여 소문자 문자열을 생성 할 수 있습니다 .

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

이것은 또한 wchar_t기반 문자열 에서도 작동합니다 .


2

해당 방법에 다음 방법을 사용하는 경우 최종적으로 선택한 방법에 대한 참고 사항 strcmp 에 일부 답변에서 제안 :

strcmp일반적으로 유니 코드 데이터에는 작동하지 않습니다. 일반적으로 strcmp바이트 당 바이트 비교 만 수행하고 utf-8로 인코딩 된 유니 코드 코드 포인트는 1 바이트를 초과 할 수 있으므로 utf-8과 같은 바이트 기반 유니 코드 인코딩으로는 작동하지 않습니다 . 유일한 특정 유니 코드의 경우 strcmp다음 바이트 당 바이트 비교가 충분하다 - 제대로 핸들은 바이트 기반 인코딩으로 인코딩 된 문자열 U + 00FF 아래에만 코드 포인트가 포함 된 경우입니다.


2

2013 년 초 현재 IBM이 유지 관리하는 ICU 프로젝트는 이에 대한 좋은 해답입니다.

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

ICU는 "업계 표준을 면밀히 추적하는 완벽한 휴대용 유니 코드 라이브러리"입니다. 문자열 비교의 특정 문제에 대해 Collation 객체는 원하는 것을 수행합니다.

Mozilla 프로젝트는 2012 년 중반 Firefox에서 국제화를 위해 ICU를 채택했습니다. 빌드 시스템 및 데이터 파일 크기 문제를 포함하여 엔지니어링 토론을 추적 할 수 있습니다.


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 라이브러리 를 사용하지 않으려면 C ++ 표준 IO 헤더 만 사용하는 솔루션이 있습니다.

#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와 같은 전역 버전을 사용하는 경우 로케일이있는 c 버전과 c ++ 버전이 있으므로 <ctype>을 포함하지 않아도됩니다. 그래서 더 나은 글로벌 버전 "::의 ToUpper ()"를 사용합니다
HaSeeB 미르

이 솔루션은 문자열 중 하나가 비어있을 때 실패합니다. ""-이 경우 false를 반환해야하는 경우 true를 반환합니다.
ekkis

0

소스 문자열을 다른 문자열과 더 자주 비교 해야하는 경우 하나의 우아한 해결책은 정규식을 사용하는 것입니다.

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

이것은 좋은 솔루션이 아니라 당신이 그것을 사용하고자하는 경우에도, 당신은 당신의 WideString으로 상수 앞에 예를 들어 L "TEST"는 L이 필요합니다
celticminstrel

누군가 그것이 왜 최악의 해결책인지 설명 할 수 있다면 좋을 것입니다. 성능 문제로 인해? 정규식을 만드는 것은 비용이 많이 들지만 나중에는 비교가 빠릅니다.
smibe

유용하고 이식성이 뛰어 나기 때문에 가장 큰 문제는 먼저 정규식에서 사용하는 문자를 포함 할 수 없다는 것입니다. 그 때문에 일반적인 문자열 비교로 사용할 수 없습니다. 또한 속도가 느려지고 smibe가 말하는 방식으로 작동하도록 플래그가 있지만 여전히 일반 기능으로 사용할 수는 없습니다.
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


a) C 함수 및 b) 이식성이 없기 때문에이 답변 외에도 stackoverflow.com/a/12414441/95309 를 읽을 가치가 있습니다.
Claus Jørgensen

이 작업을하려면 어떤 #include가 필요합니까?
ekkis 2016 년

1
@ekkis _stricmp를 사용하려면 다음에서 읽을 수있는 <string.h>를 포함해야합니다. 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;
        }
      }
}

이것은 아마도 훨씬 더 효율적으로 만들어 질 수 있지만 여기에 모든 비트가 베어 진 부피가 큰 버전이 있습니다.

모든 휴대용은 아니지만 내 컴퓨터의 모든 항목과 잘 작동합니다 (아무도, 나는 말이 아닌 그림입니다)


이것은 질문이 요구 한 유니 코드 지원이 아닙니다.
Behrouz.M

영어 이외의 문자 집합은 지원하지 않습니다.
Robert Andrzejuk

-3

소문자와 대문자로만 다른 문자열을 비교하는 쉬운 방법은 ASCII 비교를 수행하는 것입니다. ascii 테이블에서 모든 대문자와 소문자는 32 비트 씩 다르며이 정보를 사용하면 다음과 같은 정보를 얻을 수 있습니다.

    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.