C++2010. 3. 22. 10:55

프로그램을 하다보면 문자열 처리시 문자열에 포함된 특정 문자를 제거해야하는 경우가 종종있습니다.
예를들어, 어떤 명칭을 입력받으면서 공백없이 입력해달라고 했는데 사용자의 실수로 공백이 같이 입력되면
처리하는쪽에서 문자열에 공백이 있는지 여부를 체크하고 공백이 있다면 해당 공백을 제거해야합니다.
MFC를 사용하다보면 문자열 처리시에 CString 클래스를 많이 사용하는데, CString 클래스의 멤버함수중에
Remove 라는 함수가 있고 이 함수가 CString이 관리하는 문자열에서 특정 문자를 제거하는 기능입니다.
예를들면, 아래와 같습니다.
 
CString str = "Hello World!!";
str.Remove(' ');
AfxMessageBox(str);
 
이렇게 하면 결과가 아래와 같이 문자열에 공백이 제거된 상태로 메시지박스에 출력됩니다.
 
HelloWorld
 
하지만, 여기에 문제가 있습니다. 이 CString 클래스자체가 한글을 기준으로 제작된 클래스가 아니라서
한글이 포함된 문자열인 경우 Remove 함수를 사용하면 원하지 않는 결과가 발생합니다.
 
한글코드가 "한글여부(1bit) + 초성(5bit) + 중성(5bit) + 종성(5bit) = 총 2바이트(16비트)"로 구성되기
때문에 영문기준으로 1바이트씩 문자열을 비교하다보면 "디", "바"와 같이 종성이 없거나 종과 같이 종성이
존재하는 경우라도 한글문자가 공백다음에 놓이면 문자열값이 변형되는 문제가 발생합니다.
 
변경전        CString::Remove(' ')를 이용하여 변경한 후...
"다 다"    -->    "다도"
"다 바"    -->    "다뭐"
"다 종"    -->    "다좋"
 
결국 조금 불편하더라도 한글이 포함된 문자열에서 특정 문자를 제거하는 경우에는 한글을 고려해서
문자열 처리가 될수 있도록 아래와 같이 함수를 구성해서 사용하는것이 좋습니다.
 
void www_tipssoft_com_RemoveCharFromString(CString &parm_string, char parm_remove_char)
{
    int count = parm_string.GetLength(), index = 0;

    if(count > 0){
        // 문자열 제거에 사용할 버퍼를 생성한다. 이 버퍼는 기준 문자열보다 클 필요가 없다.
        char *p_data = new char[count + 1];
 
        for(int i = 0; i < count; i++){
            if(parm_string[i] & 0x80){
                // 한글인 경우는 2바이트로 구성되기 때문에 1바이트 정보인 parm_remove_char와 비교할
                // 필요가 없다. 따라서 그냥 2바이트 정보를 모두 복사해주면 된다.
                p_data[index++] = parm_string[i++];
                p_data[index++] = parm_string[i];
            } else {
                // 한글이 아닌 경우는 정보가 1바이트씩 구성되기 때문에 parm_remove_char에 지정된
                // 문자와 비교하여 다른 경우에만 복사한다.
                if(parm_remove_char != parm_string[i]) p_data[index++] = parm_string[i];
            }
        }
        // parm_remove_char를 뺀 나머지 문자가 복사된 문자열의 끝에 '\0'을 추가한다.
        p_data[index] = 0;
 
        // parm_remove_char가 제거된 문자열을 원본 문자열에 저장한다.
        parm_string = p_data;
 
        // 문자열 제거에 사용한 버퍼를 제거한다.
        delete[] p_data;
    }
}
 

사용방법 :
 
CString str = "이동 디스크";
 
www_tipssoft_com_RemoveCharFromString(str, ' ');
AfxMessageBox(str);
 
이렇게 하면 결과가 아래와 같이 문자열에 공백이 제거된 상태로 메시지박스에 출력됩니다.
( CString::Remove 함수를 사용하면 "이동도슬탤" 라고 출력됩니다. )
 
이동디스크

출처 : 팁스 소프트
http://www.tipssoft.com/bulletin/tb.php/FAQ/253

Posted by 쿵캉켕
C++2010. 3. 22. 10:51

#. 설명.
 - 다른 STL의 컨테이너 처럼 map은 많은 장점을 가진 유익한 컨테이너입니다. 빠른 Indexing을 위해서라면 STL의 컨테이너중 map을 가장 먼저 생각할 수 있겠습니다.
 - map은 Associative Container (연관컨테이너)에 속하는데 Associative Container란 키를 가지고 sort된 후에 적재되는 컨테이너로 검색속도를 빠르게 하기 위한 목적을 가진 컨테이너 입니다. map은 정렬 연관 컨테이너이며, key와 value의 쌍으로 이루어진 자료구조입니다.
 - 대부분 균형 이진 탐색 트리(balanced binary search tree)를 사용해 구현하며 추가/삭제에 O(log n)이 걸리며 검색에 있어서 탁월한 성능을 발휘합니다.

#. map의 특징에 대해서 간단히 살펴 보면 다음과 같습니다.

1. key와 value로 element를 관리할 수 있다.
2. key는 숫자 이외에도 문자열 순서를 가질 수 있는 (즉, 비교 연산자를 가지고 있는) 데이터 타입이면 모두 key가 될 수 있다.
3. key는 순서 (order)를 가지고 관리되기 때문에 검색이 빠르다.
4. key는 unique해야 한다.
5. aMapkey = value 와 같이 [] 연산자를 제공해준다.
6. Element가 추가/삭제 될 때 크기도 같이 확장 축소 된다.


#. 아래는 MSDN에 나와있는 샘플코드입니다. 참고하세요^^

Sample Code:

//////////////////////////////////////////////////////////////////////
//
// Compile options needed: None
//
// <filename> :  main.cpp
//
// Functions:
//
//      end
//      find
//      insert
//////////////////////////////////////////////////////////////////////

#pragma warning(disable:4786)

#include <iostream>
#include <string>
#include <map>

using namespace std;


typedef map<int, string, less<int> > INT2STRING;

void main()
{
// 1. Create a map of ints to strings
    INT2STRING theMap;
    INT2STRING::iterator theIterator;
    string theString = "";
    int index;

// Fill it with the digits 0 - 9, each mapped to its string counterpart
// Note: value_type is a pair for maps...
    theMap.insert(INT2STRING::value_type(0,"Zero"));
    theMap.insert(INT2STRING::value_type(1,"One"));
    theMap.insert(INT2STRING::value_type(2,"Two"));
    theMap.insert(INT2STRING::value_type(3,"Three"));
    theMap.insert(INT2STRING::value_type(4,"Four"));
    theMap.insert(INT2STRING::value_type(5,"Five"));
    theMap.insert(INT2STRING::value_type(6,"Six"));
    theMap.insert(INT2STRING::value_type(7,"Seven"));
    theMap.insert(INT2STRING::value_type(8,"Eight"));
    theMap.insert(INT2STRING::value_type(9,"Nine"));

// Read a Number from the user and print it back as words
    for( ; ; )
    {
        cout << "Enter \"q\" to quit, or enter a Number: ";
        cin >> theString;
        if(theString == "q")
            break;
        // extract each digit from the string, find its corresponding
        // entry in the map (the word equivalent) and print it
        for(index = 0; index < theString.length(); index++){
            theIterator = theMap.find(theString[index] - '0');
            if(theIterator != theMap.end() )    // is 0 - 9
                cout << (*theIterator).second << " ";
            else    // some character other than 0 - 9
                cout << "[err] ";
        }
        cout << endl;
    }
}


#. 위 소스의 출력화면 입니다.

Program Output is:

Enter "q" to quit, or enter a Number: 22
Two Two
Enter "q" to quit, or enter a Number: 33
Three Three
Enter "q" to quit, or enter a Number: 456
Four Five Six
Enter "q" to quit, or enter a Number: q

Posted by 쿵캉켕