스토리텔링 개발자

[Effective STL] 35. 대소문자 구분하지 않는 법 본문

개발/Effective STL

[Effective STL] 35. 대소문자 구분하지 않는 법

김디트 2024. 12. 24. 11:27
728x90

항목 35. 대소문자를 구분하지 않는 문자열 비교는 mismatch 아니면 lexicographical_compare를 써서 간단히 구현할 수 있다.

 

 

 

대소문자를 구분하지 않기
  • 구분하지 않는 정도를 얼만큼이나 원하는가.
    • 국제화(internationalization) 사항은 무시하고 단지 strcmp 동작 정도만 원한다면 쉽다.
    • 다국어 처리까지 원한다거나, 기본 로케일 이외의 로케일을 사용한다면 어렵다.
  • 하지만 이번 항목에서 다루는 것은 쉬운 버전이다.

 

 

 

문자열을 비교하는 인터페이스 두 가지
  1. strcmp와 비슷한 인터페이스
    • 음수, 0, 양수를 반환한다.
  2. operator<와 비슷한 인터페이스
    • true, false를 반환한다.

 

 

 

첫 번째 버전
// 대소문자 구분 없이 문자 비교 함수
int ciCharCompare(char c1, char c2)
{
    // 소문자로 바꾼다.
    // tolower의 매개 변수와 반환값은 int이지만,
    // int가 EOF가 아니기만 하면 이 값은 unsigned char가 되어도 괜찮다.
    int lc1 = tolower(static_cast<unsigned char>(c1));
    int lc2 = tolower(static_cast<unsigned char>(c2));
    
    // 비교
    if(lc1 < lc2) return -1;
    if(lc1 > lc2) return 1;
    return 0;
}

// 대소문자 구분 없이 문자열 비교 impl 함수
int ciStringCompareImpl(const string& s1, const string& s2); // 아래에서 정의

// 대소문자 구분 없이 문자열 비교 함수
int ciStringCompare(const string& s1, const string& s2)
{
    if(s1.size() <= s2.size())
        return ciStringCompareImple(s1, s2);
    else
        return -ciStringCompareImpl(s2, s1);
}

int ciStringCompareImpl(const string& s1, const string& s2)
{
    using PSCI = pair<string::const_iterator, string::cosnt_iterator>;
    
    // mismatch : 두 범위에서 첫 번째로 일치하지 않는 요소를 찾는다.
    PSCI p = mismatch(s1.begin(), s1.end(), s2.begin(), not2(ptr_fun(ciCharCompare)));
    
    // s1을 끝까지 비교했는가
    if(p.first == s1.end())
    {
        // s2도 끝까지 비교했다면 같으므로 0 리턴
        if(p.second == s2.end())
            return 0;
        else
            return -1;
    }
    
    return ciCharCompare(*p.first, *p.second);
}
  • not2(ptr_fun(ciCharCompare)
    • 이 술어는 두 개의 문자가 일치하면 true를 반환해야 한다.
    • 헌데 ciCharCompare은 -1, 0, 1을 반환하는 함수이고, 문자가 같으면 0을 반환한다.
      • c++ 컴파일러는 int를 bool로 암시적 변환 할텐데, 0은 false로 변환된다.(true여야 하는데!)
      • -1, 1 등은 true로 변환한다.(false여야 하는데!)
    • 그래서 not2와 ptr_fun을 붙였다. ptr_fun은..(항목 41 참조)

 

 

 

두 번째 버전
bool ciCharLess(char c1, char c2)
{
    return tolower(static_cast<unsigned char>(c1)) < tolower(static_cast<unsigned char>(c2));
}

// operator<처럼 동작한다.
// 즉, s1이 사전상 앞에 있다면 true, 그렇지 않으면 false
bool ciStringCompare(const string& s1, const string& s2)
{
    return lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), ciCharLess);
}
  • lexicographical_compare
    • 범위의 요소를 사전순(lexicographical)으로 비교한다. 범위의 요소를 차례로 비교하며, 번째 범위가 번째 범위보다 사전순으로 앞서면 true반환하고, 그렇지 않으면 false반환한다.
    • 즉, strcmp를 일반화한 알고리즘이다.
      • strcmp는 문자 배열에 대해서만 동작하는 반면,
        • lexicographical_compare에 들어가는 범위의 데이터는 어떤 타입도 될 수 있다.
      • strcmp는 두 문자를 코드 값으로 비교하여 같은지 작은지 큰지를 점검하는 반면,
        • lexicographical_compare의 두 요소 대소 관계 비교는 사용자가 정의할 수 있다.

 

 

 

이식성(portability)에 관해..
  • 이식성이 정말 별 상관이 없다면, 비표준으로 구현된 함수를 사용하면 된다.
    • stricmp나 strcmpi 등이 그것이다.
  • 이들은 오로지 문자열 비교만을 하도록 최적화되어 있으므로 월등히 빠르다.
728x90
Comments