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

C++빌더 팁&트릭
C++Builder Programming Tip&Tricks
[864] Ansi 및 Unicode 문자열을 고려한 프로그래밍에 대한 약간의 팁.
김태선 [cppbuilder] 24761 읽음    2009-02-28 02:29
Ansi 및 Unicode 문자열 문제는 별다른 기술적인 내용도 아니고
빌더의 경우는 변환이 너무나도 간편하고 처리하기 쉬워 별다른 설명이 필요치 않을 수도 있으나
초보나 VC를 쓰던 사람들은 양쪽 문자열 문제에 상당히 골치 아픈 코딩 경험을 하므로
간략히 빌더의 경우를 설명하겠습니다.
빌더의 경우 문자열 처리는 누워서 떡먹기처럼 쉽습니다. (누워서 떡 먹어본 사람들은 쉽지 않다고 하지만...)


빌더 2009의 가장 큰 변화라고 한다면 제공되는 모든 VCL 컴포넌트가 Unicode를 지원한다는
것입니다. 이 말은 기존의 Ansi 문자열은 물론이고 유니코드 문자열까지 지원한다는 뜻입니다.

그러므로 다음과 같이
void __fastcall TForm8::FormCreate(TObject *Sender)
{
    Caption = L"한글 ";
    Caption = "한글 ";
}
유니코드 문자열을 VCL에 대입해도, 앤시 문자열을 대입해도 다 먹습니다.
이는 모든 문자열 처리가 유니코드를 중심으로 이루어지는데,
유니코드 문자열은 기존의 앤시 문자열을 대입하면 자동으로 유니코드로 변환해서 가지게 되기 때문에
양쪽을 다 대입해도 되는 것입니다. 물론 역으로 대입해도 문제 없이 잘 됩니다.

빌더 2009에서는 유니코드 문자열 처리를 위해
UnicodeString 이라는 문자열 클래스가 추가 되었습니다.
그러므로 빌더에는 다음과 같은 3가지의 공식적인 문자열 처리 클래스가 존재합니다.
AnsiString
WideString
UnicodeString

AnsiString은 익히 보던 8Bit Char 문자열을 뜻하며,
WideString은 16Bit Char 문자열을 처리하기 위한 것입니다. WideString은
빌더 2007까지 Unicode 문자열을 처리 하기 위해 준비된 문자열 클래스입니다.
이 2가지 클래스는 기본 문자열 코드가 char나 wchar_t 의 차이가 있을 뿐 거의 모든 내용이 같습니다.
하지만 내부의 문자열의 포인트를 얻는 방법이 AnsiString.c_str() 에 비해
WideString.c_bstr() 이라는 차이를 보이게 됩니다.
즉 문자열의 포인트를 얻는 메소드 명이 틀립니다. WideString에서 그 문자열의 데이타 포인트를
얻는 메소드를 c_bstr() 이라고 한 것은 WideString 문자열을 BSTR 문자열이라고 했는데
이에 대한 포인트라는 의미로 쓰이게 됩니다.

이로 인해 AnsiString 및 WideString 양쪽에서 동작하는 라이브러리나 프로그램 제작시
약간의 불편이 존재했습니다.
또한 그외 다른 불편 사항이 있을 수 있는데, 이에 대한 프로그램을 많이 해본 것이 아니라서
그외 것은 확인 되면 추가로 리플을 달도록 하겠습니다.

그래서  빌더 2009에서는 표준명칭으로 굳어진 UnicodeString 문자열을 도입했고,
여기에는 메소드가 AnsiString과 동일하게 취급될 수 있습니다.
문자열에 대한 포인트도 UnicodeString.c_str() 식으로 얻을 수 있기 때문에,
AnsiString 및 Unicode 스트링 양쪽에서 동작하는 코드를 만드는게 용이해지게 되었습니다.

그러면 문자열을 코딩에서 다루는 방법은 크게 2가지로 나눌 수 있습니다.

1. 첫째는 AnsiString과 UnicodeString 또는 WideString 경우를 뚜렷하게 구분해서 코딩하는 것입니다.
빌더에서는 상호간에 서로 대입만 하면 자동으로 해당 문자열로 변환되므로,
이렇게 명확하게 문자열을 구분해서 코딩하는 방법은 매우 편리하며 좋은 방법입니다.
빌더 2007 이하에서는 기본 문자열 모드가 AnsiString 이므로
그대로 신경쓰지 않고 코딩하면 됩니다. 다만 나중에 빌더 2009 이상에서
그것도 Unicode 모드에서 컴파일 해야 한다면 분명히 신경을 써야 할 부분이 있습니다.
이를 위해 두번째의 공용으로 코딩하는 방법이 필요합니다.

2. 둘째는 AnsiString과 UnicodeString 공용으로 만드는 방법입니다.
즉 같은 코드로 양쪽 문자열 모드에서 컴파일 되어 동작 하도록 하는 것입니다.
또한 이와 같은 방법은 모든 빌더 버전에 공통으로 컴파일 될 수도 있으므로
한편으로 가장 바람직한 코딩 습관이기도 합니다.


//---------------------------------------------------------------------------
// CBuilder 2009를 위한 AnsiString과 WideString 간의 편리한 변환과 사용을 위한 약간의 조치.
//

// 문자열별 구분 사용, char wchar_t 를 명확히 사용
// AnsiString & UnicodeString이 호환 적합하므로 이 둘을 쓴다.
typedef    AnsiString        StringA;
typedef UnicodeString    StringU;        // 빌더 2009 이상에서만 지원.
typedef WideString        StringW;        // 빌더 2007 이하에서 사용. BSTR를 위한 특별한 경우 사용.

// Ansi & Unicode 공용, Char String 사용.
#include <TCHAR.H>    // Ansi & WideString/Unicode 공통 루틴을 처리를 위해 include.

//---------------------------------------------------------------------------
__fastcall TForm8::TForm8(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
#define add     Memo1->Lines->Add

void __fastcall TForm8::FormCreate(TObject *Sender)
{
    // Ansi 및 Unicode 구분해서 코딩하기.
    char   *bufa = "테스트 char 데이타";
    wchar_t  *bufw = _T("테스트 wchar_t 데이타");
    StringA  stra = bufa;
    StringW  strw = bufw;

    // 빌더 2009 공용으로 코딩하기. 또는 모든 빌더 버전에서 나중의 유니코드 모드를 위해 코딩하기.
    TCHAR    *s = _T("하늘 끝에서 내려온다.");
    String    ss = s;
    TCHAR    *s_ptr = ss.c_str();

    // 확인
    add(bufa);
    add(bufw);
    add(stra);
    add(strw);
    add(ss);
    add(s_ptr);

    Caption = "한글 ";
}
//---------------------------------------------------------------------------

핵심은 문자형 char, wchar_t 이 문자열 모드에 따라 적정하게 컴파일 되도록 하려면
TCHAR 형을 쓰면 되고,
문자열 모드에 따라 AnsiString 또는 UnicodeString 형으로 자동으로 인식되게 하려면
String 형 문자열 클래스를 쓰면 된다는 것입니다.
또 모든 코딩상의 문자열은 __TEXT(" 문자열......... ")
와 같이 지정하면 된다는 것입니다.

위의 예에서는, 보통 코딩상의 문자열을 나타낼때 문자열 모드에 따라 앤시 또는 유니코드 문자열로
자동으로 나타낼 수 있는 매크로로 쓰이는 __TEXT 대신  _T 가 쓰였는데,
__TEXT는 타이핑하기 너무 거추장스럽고 단순 의사코드인데 눈에 너무 띄어 간략하게 줄인 것입니다.
VC에서도  __TEXT가 거추장스러우니 줄인 _T가 가장 많이 쓰입니다.


빌더 2009에서 문자열 모드 지정은
Project -> Directories and Conditionals -> _TCHAR maps to 라는 항목에서 지정합니다.
Ansi 문자열 모드는 char를
Unicode 문자열 모드는 wchar_t 를 지정하게 되어 있습니다.
빌더 2009 미만에서는
Project -> Directories and Conditionals -> Conditionals 에
;_UNICODE;UNICODE 를 추가해주면 유니코드 모드로 프로그램을 만들 수 있습니다.
VC에서도 UNICODE(WinAPI용) 과 _UNICODE (표준 C/C++ 함수용)를 함께  지정합니다.


빌더 2007 이하에서  코딩할때
앤시 및 유니코드문자열 양쪽에서 동작하는 코드를 만들때 특히 WideString용을 중심으로 코딩을 만들 때는
빌더 2009와 유사하지만, 단 WideString에는 c_str()  메소드가 없으므로,
~ 연산자를 정해서 WideString.c_bstr() 이 리턴되도록 하고
기존 AnsiString의 경우도
~ 연산자를 정해서 AnsiString.c_str() 이 리턴되도록 한다면
역시 동일한 코드로 양쪽 문자열에서 다 동작하는 코드를 만들 수 있습니다.
즉 ~문자열클래스 식으로 문자열 포인트를 얻도록 코딩하면
양쪽에서 다 문제없이 동작하는 코드를 만들 수 있는 것이죠.

//---------------------------------------------------------------------------
//  String을 ~ 연산자를 통해 TCHAR* 를 바로 얻는 기법.

inline char* __fastcall operator ~(const AnsiString& s)
{
    return s.c_str();
}

inline wchar_t* __fastcall operator ~(const WideString& s)
{
    return s.c_bstr();
}
//---------------------------------------------------------------------------

빌더 2009 이상을 위해서는 다음과 같이 UnicodeString에 대해서
~연산자를 오버로딩 해 놓으면 코딩하기 매우 편리합니다.

//---------------------------------------------------------------------------
// 빌더2009 이상에서 사용.

inline wchar_t* __fastcall operator ~(const UnicodeString& s)
{
    return s.c_str();
}
//---------------------------------------------------------------------------




위까지는 너무 기초적인 내용이고, 공용으로 만드는 것도 아무런 문제가 되지 않는 것 처럼 보입니다.
하지만 이렇게 문자열을 대입하는 수준이라면 그렇지만 문자열을 섬세하게 조작해야 한다면,
조금 골치가 아프게 됩니다. 그야 말로 누워서 떡 먹는 것은 조금 힘들다는 것을 깨닫게 됩니다.


왜냐하면 실지로 문자열을 다룰때 필요한 함수를 쓸때 문제가 됩니다.
가령 strcpy strcat ... 등은 모두 char 문자열에 대한 서비스이므로,
wchar_t 에 대한 서비스로 사용할 수 없으며,
wchar_t 를 위해서는 wcscpy wcscat ... 식의 함수가 준비되어 있습니다.
str -> wcs 로 바꾼다고만 기억하면 기존 함수명과 동일합니다.
wcs 는 Wide Char String 이라는 뜻입니다.

그러면 Ansi & Unicode 양쪽에서 모두 동작하는 문자열 함수 시리즈는 뭘까요?
이를 위해 반드시
#include <TCHAR.H>    // Ansi & WideString/Unicode 공통 루틴을 처리를 위해 include.
를 포함해주어야 합니다.
TCHAR.H는 문자 코드 양쪽에서 모두 쓸수 있는 함수의 정의를 담고 있는 파일로
빌더 6, 2006, 2007, 2009 모두 있으며, 여기에는

_tcs.... 시리즈가 있는데 이것이 모두 기존
str... & wcs... 에 대응합니다. _tcs 는 _TCHAR String 이라는 뜻입니다. _TCHAR 는 TCHAR와 동일힙니다.
그러므로,

strcpy -> _tcssty
strcat -> _tcscat

식으로 사용할 수 있습니다.
한번 TCHAR.H를 열어 문자열 처리 함수를 살펴 보면 사용할 수 있는 문자열 함수 리스트가 나오니
이를 주지하면 됩니다. 이러한 내용은 VC도 동일합니다.

void __fastcall TForm8::FormCreate(TObject *Sender)
{
    String    ss = _T("하늘 끝에서 내려온다.");
    TCHAR    *s_ptr = ss.c_str();

    TCHAR    buf[100];
    _tcscpy(buf, s_ptr);
}


그런데 이렇게 양쪽에서 동작하는 루틴을 만들때 공용으로 만들기 위해서는
새로운 대체 함수를 주지해야 하는데 _tcs... 식의 이름을 쓴 함수를 쓰면 된다고는 하지만
TCHAR.H와 Help를 보며 이를 일일이 확인하며 프로그래밍하는 것은 때로는 반드시 필요하지만
때로는 조금 귀잖은 일이기도 합니다. 
이를 때는 확실하게 기본 문자열을 Ansi형태로 바꾼 다음 처리하고 그것을 다시
원하는 문자열 형태로 되돌리는 것이 프로그래밍의 속도 면에서는 유리하지만
Ansi 문자열 모드에서 컴파일 될때는 변환하지 않아도 루틴으로  몇 바이트씩의 추가 코드가 들어가게 되죠.
{
    // 입력
    AnsiString   ss = input_str;

    // 작업
    char     *s_ptr = ss.c_str();
    char    buf[100];
    strcpy(buf, s_ptr);
   
    // 리턴
    String ret_str = buf;
    return ret_str;
}

실제 코딩에 있어 그다지 프로그래밍의 앞길을 가로막을 사항은 아니고
단시간에 해결될 수 있는 문제이므로, 어떤 식으로 처리하던지 선택은 자유라고 하겠습니다.

깔끔하게 양쪽에서 동작하는 루틴을 만들려면 반드시 TCHAR.H 안에 선언된
_tcs... 시리즈 문자열 함수를 한번 눈여겨 보고 이를 이용하여 코딩하도록 하고,
귀잖으면 위처럼 하면 됩니다.


C++빌더나 VC등의 개발툴과 윈도우 API에서는
UNICODE가 쓰인다면 그것은 UTF-16 형식이고,
이는 곧 WideString과도 같다고 생각하면 됩니다.
UNICODE라는 것은 문자 체계에 대한 하나의 정의이고,
이를 메모리상에서 어떤 식으로 표현할지에 대한 방법으로
UTF-16 또는 UCS-2 라는 형태로 쓰이는 것입니다.
필자가 Ansi 문자열이라고 표현했고 흔히 그렇게 표현하는 문자열은
사실은 MBCS(Multi Byte Charecter Set) 형태로
영문등 ascii code 127 이하는 1바이트로 표현하고, 한글 등 2바이트 체계 문자는 2바이트로 표현하는
방식을 말하는 것인데,  완성형 한글을 쓰는 우리가 늘상 흔히 보고 다루는 문자열 체계이고
Ansi String이라는 명칭에서도 알수 있듯이 여기에 대한 통칭으로 많이 사용되므로 그렇게 표현했습니다.

문자열 체계에 대해서는 지금까지 오랜시간 변천사를 겪어서
인터넷에서 자료를 찾아 한번 정도 정확하게 주지해 두는 것이 좋습니다.


문자열 문제에 대해 전체적인 내용이 아니라서 약간의 팁이라고 제목을 달았는데
내용이 충실하지 못해 지송합니다.
그럼..
초보대왕 [sauron]   2009-02-28 17:26 X
좋은 내용입니다. 근데 빌더의 UNICODE 라는 것이 UTF16 과 동일한 것인가요?
VC++ 에서는 UNICODE 가 지원되지만 이게 UTF16 이라고는 명시하지 않고 있읍니다.
UNICODE 를 지원한다는 것이 약간 두리뭉실한 듯 해서 질문드려 봅니다.
김태선 [cppbuilder]   2009-03-01 18:42 X
UNICODE는 문자열 체계에 대한 정의일 뿐입니다.
이것이 메모리에서 어떤 식으로 표현되느냐(엔코딩 되어 있느냐)에 따라 여러가지 방식이 존재하는데
일반적으로 윈도우에서 UNICODE라 함은 UTF16으로 생각하시면 됩니다.
빌더/VC++ 모두 같은 사항입니다.
초보대왕 [sauron]   2009-03-02 05:26 X
-- 일반적으로 윈도우에서 UNICODE라 함은 UTF16으로 생각하시면 됩니다.
-- 빌더/VC++ 모두 같은 사항입니다

아 네,알겠읍니다.^^

+ -

관련 글 리스트
864 Ansi 및 Unicode 문자열을 고려한 프로그래밍에 대한 약간의 팁. 김태선 24761 2009/02/28
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.