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

C++빌더 강좌/문서
C++Builder Programming Tutorial&Docments
[169] C++빌더의 유니코드 지원 Part II: 유니코드 지원을 위한 새 RTL 기능들과 클래스들
박지훈.임프 [cbuilder] 21465 읽음    2008-09-23 09:13
작성자 : 닉 하지스(Nick Hodges)

요약: 이 아티클에서는 유니코드 스트링을 다루기 위한 C++빌더 2009 런타임 라이브러리의 새로운 기능들을 다룹니다.


서론



파트 I 에서는, 유니코드 세계의 모든 문자 셋으로 통신할 수 있게 됨으로써 유니코드 지원이 어떻게 C++빌더 개발자들에게 큰 이점이 되는지 살펴봤습니다. UnicodeString 타입의 기본적인 내용들과 C++빌더에서 어떻게 사용되는지 알아봤습니다.

파트 II에서는, 유니코드와 일반 스트링 처리를 지원하는 런타임 라이브러리의 새로운 기능들 중 일부를 살펴볼 것입니다.

TCharacter 클래스



티뷰론 RTL은 TCharacter라는 새로운 클래스를 포함하고 있습니다. 이 클래스는 Character 유닛에 있습니다. 이 클래스는 완전히 static 함수들로만 이루어진 sealed 클래스입니다. 개발자들은 직접 TCharacter 객체를 생성해서는 안되고, 그 static 메소드만을 호출할 수 있습니다. 이들 static 함수들은 다음의 기능을 포함한 여러 작업을 할 수 있습니다.

  • 문자들을 대문자 혹은 소문자로 변환
  • 지정한 문자가 특정 타입, 예를 들어 글자, 숫자, 마침표인지 확인

    TCharacter는 유니코드 컨소시엄에 의해 수립된 표준을 사용합니다.

    개발자들은 이전에는 문자의 단순 비교로 했던 많은 작업을 TCharacter 클래스를 이용하여 할 수 있습니다. 예를 들어, 아래 코드는,
    {
        if(MyChar>='0' && MyChar<='9')
            ...
    }
    

    다음과 같이 간단히 치환될 수 있습니다.
    #include 
    
    {
        if(TCharacter::IsDigit(MyChar))
            ...
    }
    

    TCharacter 클래스에는 IsDigit()과 비슷한 IsNumber() 멤버 함수도 있습니다. IsDigit()은 0~9까지의 문자만 true로 인식하지만, IsNumber()은 0~9 외에 유니코드에서 지정하는 number 문자들이 더 포함됩니다. 예를 들어 ¹, ², ³ 같은 첨자와 ½, ¼같은 분수 문자 등도 IsNumber()에서 true를 리턴합니다.

    또, IsLetter() 멤버 함수는 영문 알파벳 문자를 포함한 한글 문자 등 유니코드에서 letter로 지정된 문자들에 대해 True를 리턴합니다. 예를 들어 ‘a’와 ‘임’ 문자는 모두 IsLetter()에서 true를 리턴합니다.

    Character 유닛은 또한 TCharacter 클래스의 static 함수들의 기능을 래핑한 여러 단독 함수들을 포함하고 있으므로, 여러분이 단순 함수 호출을 더 선호한다면 위의 코드를 다음과 같이 작성할 수도 있습니다.
    #include 
    
    {
        if(IsDigit(MyChar))
            ...
    }
    

    이와 같이 TCharacter 클래스는 여러분이 필요로 할 만한 거의 모든 문자 조작 및 확인 작업을 위해 사용할 수 있습니다.

    추가로, TCharacter는 지정한 문자가 서로게이트 페어(surrogate pair)에서 하이 혹은 로우 서로게이트인지를 확인하기 위한 static 멤버 함수들도 포함하고 있습니다.

    TEncoding 클래스



    티뷰론 RTL은 또한 TEncoding라는 새로운 클래스도 포함하고 있습니다. 이 클래스의 목적은 특정 문자 인코딩 타입을 지정하여 해당 상황에서 여러분이 VCL에 어떤 타입의 인코딩을 사용할지를 알려주기 위한 것입니다.
    예를 들어, 여러분이 텍스트를 포함한 TStringList 객체를 가지고 있고 파일로 저장하려고 한다고 합시다. 이전에는 다음과 같이 코딩 했을 것입니다.
    {
        ...
        MyStringList->SaveToFile("SomeFilename.txt");
        ...
    }
    

    그러면 파일은 기본 ANSI 인코딩으로 쓰여지게 됩니다. 이 코드는 여전히 제대로 동작하여 이전처럼 ANSI 스트링 인코딩으로 파일을 쓰게 되지만, C++빌더는 이제 유니코드 스트링 데이터를 지원하므로 개발자들은 스트링 데이터를 특정 인코딩으로 저장하기를 원할 수도 있습니다. 그래서 SaveToFile(LoadFromFile도 마찬가지) 함수는 사용할 인코딩을 지정하는 두 번째 옵셔널 파라미터를 받을 수 있습니다.
    {
        ...
        MyStringList->SaveToFile("SomeFilename.txt", TEncoding::Unicode);
        ...
    }
    

    위의 코드를 실행하면 파일은 유니코드(UTF-16) 인코딩의 텍스트 파일로 쓰여지게 됩니다. TEncoding은 지정한 바이트 셋을 한 인코딩으로부터 다른 인코딩으로 변환하는 기능, 지정한 문자열 혹은 문자 배열의 바이트들과 문자들에 대한 정보를 얻어오는 기능, 어떤 스트링이든 array of byte (TBytes)로 변환하는 기능, 특정 문자열 혹은 문자 배열의 특정 인코딩을 위해 필요할 수 있는 기타 기능들을 가지고 있습니다.

    TEncoding 클래스는 지정된 인코딩의 TEncoding 객체에 대해 싱글턴 액세스를 할 수 있는 다음과 같은 클래스 속성들을 가지고 있습니다. (SysUtils.hpp)
    /* static */ __property TEncoding* ASCII = {read=GetASCII};
    /* static */ __property TEncoding* BigEndianUnicode = {read=GetBigEndianUnicode};
    /* static */ __property TEncoding* Default = {read=GetDefault};
    /* static */ __property TEncoding* Unicode = {read=GetUnicode};
    /* static */ __property TEncoding* UTF7 = {read=GetUTF7};
    /* static */ __property TEncoding* UTF8 = {read=GetUTF8};
    

    Default 속성은 ANSI 액티브 코드페이지를 가리킵니다. Unicode 속성은 UTF-16을 가리킵니다.

    TEncoding에는 다음의 함수도 포함되어 있습니다.
    static TEncoding* __fastcall GetEncoding(int CodePage);
    

    이 함수는 파라미터로 넘겨진 코드페이지와 관련이 있는 TEncoding 객체를 리턴합니다.

    다음의 함수도 있습니다.
    virtual TBytes __fastcall GetPreamble(void) = 0 ;
    

    이 함수는 지정한 인코딩에 대한 바른 BOM을 리턴합니다.

    TEncoding은 또한 .NET 클래스 Encoding과도 인터페이스 호환이 됩니다.

    TStringBuilder



    RTL에는 TStringBuilder라는 새 클래스가 추가되었습니다. 이 클래스의 목적은 그 이름에서 짐작할 수 있는데, 스트링을 만들어내기(“build up”) 위해 설계된 것입니다. TStringBuilder는 지정한 스트링에 내용을 추가, 치환, 삽입하기 위한 여러 오버로드된 함수들을 가지고 있습니다. TStringBuilder 클래스는 다양한 다른 데이터 타입들로부터 단일 스트링을 생성하는 작업을 간단하게 해줍니다. 모든 Append, Insert, Replace 함수들은 TStringBuilder 객체를 리턴하므로, 단일 스트링을 만들기 위해 간단히 되풀이 연결하여 사용할 수 있습니다.

    예를 들면, 복잡한 Format 문 대신에 TStringBuilder를 사용할 수 있습니다. 예를 들어 다음과 같은 코드가 가능합니다.
    void __fastcall TForm86::Button2Click(TObject *Sender)
    {
        TStringBuilder *MyStringBuilder = new TStringBuilder("");
        try
        {
            double Price = 2000;
            Label1->Caption = MyStringBuilder->Append(String("이 사과는 kg 당 "))->Append(Price)->Append(String("원입니다."))->ToString();
        }
        __finally
        {
            delete MyStringBuilder;
        }
    }
    

    또한 TStringBuilder는 .NET 클래스 StringBuilder와 인터페이스 호환이 가능합니다.


    새로운 스트링 타입의 선언



    티뷰론의 컴파일러는 지정한 코드페이지와 연관되는 스트링 타입을 개발자가 직접 선언할 수 있게 해줍니다. 수많은 코드페이지들이 준비되어 있습니다. (MSDN에 상세한 코드페이지 리스트가 있습니다) 예를 들어, ANSI-Cyrillic과 연결되는 스트링 타입이 필요하다면 다음과 같이 선언할 수 있습니다.
    // ANSI-Cyrillic의 코드 페이지는 1251임
    typedef AnsiStringT<1251> CyrillicString;
    

    이 새로운 스트링 타입은 Cyrillic 코드 페이지와 연결됩니다.


    유니코드 지원 RTL 추가 루틴들



    RTL에는 유니코드 스트링을 지원하는 다른 많은 루틴들이 있습니다.

    StringElementSize() 함수와 ElementSize() 멤버 함수



    StringElementSize()는 지정한 스트링의 엘레먼트(코드 포인트)의 단위 크기를 리턴합니다. 또, Ansistring과 UnicodeString 클래스에 추가된 ElementSize() 멤버 함수도 같은 역할을 합니다. 다음 코드를 살펴보시기 바랍니다.
    void __fastcall TForm88::Button3Click(TObject *Sender)
    {
        AnsiString A = "이것은 AnsiString입니다";
        Memo1->Lines->Add("AnsiString의 ElementSize: " + IntToStr(A.ElementSize()));
        UnicodeString U = "이것은 UnicodeString입니다";
        Memo1->Lines->Add("UnicodeString의 ElementSize: " + IntToStr(U.ElementSize()));
    }
    

    위 코드의 결과는 다음과 같습니다.
    AnsiString의 ElementSize: 1
    UnicodeString의 ElementSize: 2
    

    (StringElementSize() 단독 함수는 UnicodeString과 RawByteString파라미터 버전의 두 함수로 오버로드되어 있습니다. Ansistring버전의 오버로드 함수가 없기 때문에, Ansistring 파라미터로 호출하는 경우 컴파일 타임에 두 오버로드 함수 사이에서 모호성 (Ambiguity) 에러가 발생하는 문제가 있습니다. 이 문제는 코드기어 퀄리티센트럴에 버그로 레포트한 상태입니다.)

    StringCodePage() 함수와 CodePage() 멤버 함수



    StringCodePage는 지정한 스트링의 코드페이지에 해당하는 Word 값을 리턴합니다. 또, Ansistring과 UnicodeString 클래스에 추가된 CodePage() 멤버 함수도 같은 역할을 합니다. 다음 코드를 살펴보세요.
    // ANSI-Cyrillic의 코드 페이지는 1251임
    typedef AnsiStringT<1251> CyrillicString;
    
    void __fastcall TForm88::Button2Click(TObject *Sender)
    {
        AnsiString A = "이것은 AnsiString입니다";
        Memo1->Lines->Add("AnsiString 코드페이지: " + IntToStr(A.CodePage()));
        UnicodeString U = "이것은 UnicodeString입니다";
        Memo1->Lines->Add("UnicodeString 코드페이지: " + IntToStr(U.CodePage()));
        UTF8String U8 = "이것은 UTF8string입니다";
        Memo1->Lines->Add("UTF8string 코드페이지: " + IntToStr(U8.CodePage()));
        CyrillicString C = "이것은 CyrillicString입니다";
        Memo1->Lines->Add("CyrillicString 코드페이지: " + IntToStr(C.CodePage()));
    }
    

    한글 윈도우 버전에서 실행한 위의 코드의 결과는 다음과 같습니다. (영문 윈도우 버전에서는 AnsiString의 코드 페이지가 1252로 나타납니다)
    AnsiString 코드페이지: 949
    UnicodeString 코드페이지: 1200
    UTF8string 코드페이지: 65001
    CyrillicString 코드페이지: 1251
    

    (StringCodePage() 단독 함수도 역시 StringElementSize() 함수와 마찬가지의 문제가 있습니다. Ansistring버전의 오버로드 함수가 없기 때문에, Ansistring 파라미터로 호출하는 경우 컴파일 타임에 두 오버로드 함수 사이에서 모호성 (Ambiguity) 에러가 발생하는 문제가 있습니다. 마찬가지로 코드기어 퀄리티센트럴에 버그로 레포트한 상태입니다.)

    유니코드를 위한 기타 RTL 기능들



    스트링을 한 코드페이지에서 다른 코드페이지로 변환하기 위한 여러 다른 함수들이 있으며, 다음의 함수들은 그 일부입니다.
    UnicodeStringToUCS4String()
    UCS4StringToUnicodeString()
    UnicodeToUtf8()
    Utf8ToUnicode()
    

    또한 RTL에는 인코딩이 연관되지 않은 스트링을 위한 RawByteString이라는 타입도 선언되어 있습니다. (system.hpp)
    typedef AnsiStringT<65535> RawByteString;
    

    RawByteString 타입의 목적은 모든 코드페이지의 스트링 데이터를 코드페이지 변환 없이 전달하는 것입니다. 이것은 바이트 기반 스트링 검색처럼 특정 인코딩을 고려하지 않는 루틴에 가장 유용합니다. 보통은, 이것은 코드페이지에 대한 고려 없이 스트링을 처리하는 루틴의 파라미터들은 RawByteString이어야 한다는 것을 의미합니다. 스트링 변수를 선언할 때 RawByteString 타입으로 하는 경우는 드물겠지만, 그런 경우가 있다면 예기치 않은 동작을 하거나 잠재적으로 데이터가 손실될 수도 있다는 것을 염두에 두어야 합니다.

    일반적으로, 스트링 타입들은 대입 연산에서 서로 호환됩니다.

    예를 들면 다음의 코드는 예상하는 대로 동작합니다.
    MyUnicodeString = MyAnsiString;
    

    AnsiString의 내용을 받아 UnicodeString에 넣습니다. 일반적으로 한 스트링 타입을 다른 스트링 타입에 대입하는 것이 가능하며, 컴파일러가 변환에 필요한 작업을 수행합니다. 물론 가능한 경우입니다.

    하지만, 일부 변환은 데이터 손실을 일으킬 수도 있으며, 유니코드 데이터를 가진 한 스트링 타입으로부터 다른 스트링으로 데이터를 넣을 때는 이런 데이터 손실이 일어나지 않는지 주의해야 합니다. 예를 들어, UnicodeString을 AnsiString에 대입할 수 있지만, 만약 UnicodeString이 런타임에 액티브 ANSI 코드페이지에 매핑되지 않은 문자를 포함하고 있다면, 그 문자들은 변환 과정에서 손실될 수 있습니다. 다음의 코드를 살펴봅시다.
    void __fastcall TForm88::Button4Click(TObject *Sender)
    {
        UnicodeString U = "이것은 UnicodeString입니다";
        AnsiString A = U;
        Memo1->Lines->Add(A);
        U = "Добро пожаловать в мир Юникода с использованием Дельфи 2009!!";
        A = U;
        Memo1->Lines->Add(A);
    }
    

    OS 코드페이지가 1252인 영문 윈도우의 경우, 위 코드의 결과는 다음과 같습니다. (한글 윈도우의 경우 코드페이지는 949이며 Cyrillic 문자들이 제대로 출력됩니다)
    이것은 UnicodeString입니다
    ????? ?????????? ? ??? ??????? ? ?????????????? ?????? 2009!!
    

    보다시피, Cyrillic 문자들은 Windows-1252에 매핑되지 않으므로 이처럼 UnicodeString에서 AnsiString으로 대입할 때 정보가 손실됩니다. 알아볼 수가 없는 형태로 나타나는 이 결과는 UnicodeString이 AnsiString의 코드페이지에서는 나타낼 수 없는 문자들을 포함하고 있기 때문으로, 해당 문자들은 UnicodeString에서 AnsiString으로 대입될 때 손실되고 물음표로 대체됩니다.

    SetCodePage()



    SetCodePage()는 System.hpp 헤더에 다음과 같이 선언되어 있습니다.
    extern PACKAGE void __fastcall SetCodePage(RawByteString &S, Word CodePage, bool Convert = true);
    

    이 함수는 지정한 AnsiString에 새 코드페이지를 지정하는 새 RTL 함수입니다. 옵셔널 파라미터인 Convert는 스트링의 내용이 지정한 코드페이지로 변환될 것인지의 여부를 결정합니다. Convert 파라미터가 false이면 단순히 지정한 스트링의 코드페이지만 변경됩니다.

    SetCodePage()는 가급적 적게, 그리고 아주 조심해서 사용해야 합니다. 지정한 코드페이지가 스트링 내용과 매칭되지 않을 경우(예를 들면 Convert 가 false로 지정된 경우), 예측할 수 없는 결과가 발생할 수 있습니다. 마찬가지로, 스트링의 기존의 데이터가 변환되고 새로운 코드페이지가 원래의 문자에 대한 표현이 없을 경우 데이터 손실이 발생할 수 있습니다.

    스트링의 TBytes 값 얻어내기



    또한 RTL에는 스트링으로부터 바이트 배열을 얻어내기 위한 오버로드된 함수들이 추가되었습니다. 파트 III에서 살펴보겠지만, 데이터 버퍼로 이용하는 목적으로는 스트링보다는 TBytes를 사용할 것을 권합니다. RTL은 여러 다른 스트링 타입들을 파라미터로 받는 오버로드된 BytesOf()함수들을 제공하여 작업을 쉽게 해줍니다.


    결론



    티뷰론의 런타임 라이브러리는 이제 새로운 UnicodeString을 완벽하게 지원할 수 있습니다. RTL에는 코드페이지를 관리하고 이전 버전들로부터의 소스 이전을 쉽게 해주기 위해 유니코드 스트링을 다루고 처리하고 변환하기 위한 새 클래스들과 새 루틴들이 포함되어 있습니다.

    파트 III에서는, 여러분의 코드가 유니코드에 대비가 될 수 있도록 하기 위해 알아야 할 몇몇 코드들을 살펴보겠습니다.


    원문 : http://dn.codegear.com/article/38498
    (델파이 관점에서 작성된 원문을 C++빌더 관점으로 편역했습니다.)
  • 웨잇포림 [bongsaja]   2008-10-12 18:16 X
    좋은 정보에 늘 감사드립니다.
    정한경 [firezero102]   2009-10-28 11:12 X
    감사합니다.. ^^

    + -

    관련 글 리스트
    169 C++빌더의 유니코드 지원 Part II: 유니코드 지원을 위한 새 RTL 기능들과 클래스들 박지훈.임프 21465 2008/09/23
    Google
    Copyright © 1999-2015, borlandforum.com. All right reserved.