Turbo-C
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
터보-C 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
Lua 게시판
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C/C++ 팁&트릭
[19] [STL]大小文字를 구분하지 않는 문자열 비교법(모든 유럽어에서 가능)
김백일 [cedar] 7605 읽음    2002-06-24 14:53
안녕하세요! 김백일입니다.

지난번(16번) 팁에서 대소문자 변환 방법에 대해 다루었습니다.

계속해서 이번에는 Scott Meyers의 'Effective STL'의 부록 A에 있는
'大小文字를 구분하지 않는 문자열 비교법'에 대해서 간단히 정리하겠습니다.
이 부분은 Scott Meyers의 글은 아니고요, Matt Austern의 글이 실려있는 겁니다.

=========================================================================

대소문자를 구분하지 않는(without case, ignore case 또는 case-insensitive)
문자열 비교방법을 구현하는 방법은,
물론 ANSI C에서 지원하는 stricmp() 또는 strcmpi()가 있지만,
(사실, 두 영어 문자열만 비교할 때는 이 함수가 가장 빠릅니다.)
ANSI C의 약점인 로케일 처리의 문제점이 있습니다.

우선 생각할 수 있는 방법은,
아예 대소문자 구분이 없는 문자열 클래스를 따로 만드는 것입니다.

ANSI C++의 string은
basic_string<char, char_traits<char>, allocator<char> >의
다른 이름일 뿐입니다.
그러므로 char_traits 를 다르게 정의한 문자열 클래스를 만들어서
구현하는 방법이 있습니다.

실제로 이렇게 구현한 예제는
Herb Sutter의 'Exceptional C++'의 Item 2와 3(pages 4-9)에 자세히 나와있습니다.

그러나 이러한 방법에 대해서 Matt Austern은 별로 가치가 없는 방법이라고
혹평하고 있습니다. 그 이유를 정리하면 다음과 같습니다.

1) 표준 라이브러리의 I/O 스트림 클래스를 사용하기가 불편하다.
2) 하나의 객체가 경우에 따라 달리 작동하는 방식이어야 한다. -> 경우에 따라 별도의 타입을 만드는 것은 오히려 불편함을 가중시킨다.
3) 표준 라이브러리의 방식에 자연스럽지 않다 -> char_trait은 상태가 없는 타입
4) std::search나 std::find_end 와 같은 일반 알고리듬을 사용할 수 없다.

그러므로, Austern은 표준 라이브러리의 설계 철학과 자연스럽게 맞는 방법을 제시했는데요,
이는 대소문자를 구분하지 않는 함수 객체를 사용하는 방법입니다.
예를 들어 다음과 같이 사용하는 방법이지요.

sort(string_container.begin(), string_container.end(), lt_str);
// lt는 less than의 의미입니다. 즉, operator< 의 의미(기능)을 하도록 해야 합니다.

여기서부터는 이 함수 객체를 만드는 방법에 대해 설명하겠습니다.

일단, 영어의 경우만 고려한다면 상당히 간단한 방법으로 구현할 수 있습니다.

[방법 1] stricmp()/strcmpi()를 사용한 방법(C 방식)

#include <cstring> // ANSI C 라이브러리의 string.h

struct lt_eng_str : public binary_function<string, string, bool>
{
  bool operator() (const string& x, const string& y) const
  {
    return stricmp(x.c_str(), y.c_str()) == -1;
  }
};

[방법 2] ::toupper()를 사용한 방법(C++ 방식)

struct lt_eng_str : public binary_function<string, string, bool>
{
  struct lt_eng_char : public binary_function<char, char, bool>
  {
    bool operator() (char x, char y) const
    { // 아래 표현은 toupper((unsigned char)(x)) < toupper((unsigned char)(y)) 와 같지만
      return toupper(static_cast<unsigned char>(x)) < // C++에서는 static_cast를 명시적으로
           toupper(static_cast<unsigned char>(y));    // 쓰는 것이 권장 사항입니다.
    }
  };

  bool operator() (const string& x, const string& y) const
  {
    return lexicographical_compare(x.begin(), x.end(), y.begin(), y.end(), lt_eng_char());
  }
};

그러나 이전 글(16번)에서 썼듯이 영어가 아닌 다른 유럽어(독일어, 러시아어 등)에서는
제대로 작동하지 않는다는 단점이 있습니다.
다른 유럽어에서도 제대로 작동하도록 고쳐져야 합니다.

[방법 3] 모든 문자에 대해 ctype::toupper()로 대문자로 변환한 다음,
그 결과를 캐싱해두고 변환함.

struct lt_str : public binary_function<string, string, bool>
{
  struct lt_char
  {
    const char *tab;
    lt_char(const char *t) : tab(t) {}
    bool operator() (char x, char y) const
    {    
      return tab[x - CHAR_MIN] < tab[y - CHAR_MIN];
    }
  };

  char tab[CHAR_MAX - CHAR_MIN + 1];
  // 모든 문자를 저장하는 문자 배열입니다.
  // 파스칼같으면
  // tab : array[CHAR_MIN .. CHAR_MAX] of char;
  // 로 쓰면 간단하지만,
  // C/C++에서는 파스칼처럼 배열 첨자의 최소값을 지정할 수 없으므로,
  // 이런식으로 할 수 밖에는 없겠죠. -_-a

  lt_str(const locale& L = locale::classic())
  {
    const ctype<char>& ct = use_facet<ctype<char> >(L);
    for (int i = CHAR_MIN; i <= CHAR_MAX; ++i)  // cast하기 귀찮아도 for문에서는 char보다 int가 빠릅니다.
      tab[i - CHAR_MIN] = static_cast<char>(i); // (char)i 와 같은 표현
    ct.toupper(tab, tab + CHAR_MAX - CHAR_MIN + 1);
  }

  bool operator() (const string& x, const string& y) const
  {
    return lexicographical_compare(x.begin(), x.end(), y.begin(), y.end(), lt_char(tab));
  }
};

+ -

관련 글 리스트
19 [STL]大小文字를 구분하지 않는 문자열 비교법(모든 유럽어에서 가능) 김백일 7605 2002/06/24
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.