본문 바로가기

Programming

유니코드 프로그래밍시 읽어둘만한 것들 1

포켓PC에서는 모든 API는 유니코드 스트링으로 작업을 합니다.

하지만, 과거 DeskTop시절. 우리는 개발시에는 ANSI문자열을 사용하는데 더욱더 익숙해져 있습니다. 그래서 이번내용을 자세히 모르고 넘어가면 나중에 진짜 고생을 하게 될 것입니다. 꼭 자세히 보고 해보시길.

자. 우선 자세히 보기전에 전체적인 내용을 보도록 하죠.

 Ps. 다음에 나오는 내용중에 외국 칼럼리스트가 쓴 내용도 있습니다.

 ► ANSI 문자열?

 일반적으로 우리가 제일 많이 쓰는걸 한번 보죠.

 AfxMessageBox(“sss”);

TRACE(“ddd”);

 다음 코드는 WinAPI 이므로 MFC 에서는 가능하지만, CE시에서는 제대로 작동을 안하죠.

 AfxMessageBox(_T(“sss”));

TRACE(_T(“ddd”));

 ► _T, TEXT, L””

 _T와 TEXT매크로는 PPC, DeskTop 모두 동작을 합니다. 이 매크로는 시스템의 유니코드를 만드는 것으로 유니코드 기반(PPC, NT등) 그리고 WinAPI 에서 유니코드를 사용 못하는 시스템에서(Win95,98) ANSI문자열을 만들어 줍니다.

물론 L도 standard C++에서의 접두어로, 유니코드를 만들어 주지만, _T매크로를 쓰는 것이 DeskTop과 PPC 작업시 오는 혼동을 없애므로 추천합니다.

 ► ANSI를 Unicode로 변환

 때떄로 여러분들은 ANSI문자열을 접하게 되고 이것을 유니코드로 변환해야 할 경우가 생기죠.

대개 이것은 ANSI 문자열을 사용하는 서드파티 라이브러리에서 사용하게 될 때인데요,

대부분 CString 를 사용하면 편리합니다.

 Char *pAnsiStr = “Ansi String”;

CString strUnicode = pAnsiString;

 ► Unicode <=> ANSI

 유니코드를 ANSI로 변환하는 방법은 많습니다. 하지만 이 작업을 하기전에 코드페이지는 무엇이고, 제대로 바뀌지 않는 ANSI 는? 뭘까 이런거를 봐야 나중에 디버깅시에 고생을 안하죠.

 - wcstombs

이 함수는 간단히 ANSI문자열을 Unicede로 변경하는 가장 간단한 방법입니다.

하지만 이것은 코드 페이지 같은 파라미터가 없어서 별로…

 char* GetAnsiString(const CString &s)

{

int nSize = s.GetLength();

      char *pAnsiString = new char[nSize+1];

      wcstombs(pAnsiString, s, nSize+1);

      return pAnsiString;

}

 CString strUnicode = _T("Some test string");

char *pAnsiString = GetAnsiString(strUnicode);

 ► WideCharToMultiByte

WideCharToMultiByte는 여러 파라미터를 가지고 강력한 기능을 제공합니다.

이 함수는 모든 것을 조정할 수 있게 해줍니다: 코드 페이지, default character, ……

아주 중요하므로 익숙해지시길.

 char* GetAnsiString(const CString &s, UINT nCodePage)

{
      int nSize = s.GetLength();

      char *pAnsiString = new char[nSize+1];

       WideCharToMultiByte(nCodePage, 0, s, nSize+1, pAnsiString, nSize+1, NULL, NULL);

      return pAnsiString;
}

 CString strUnicode = _T("Some test string");

char *pAnsiString = GetAnsiString(strUnicode, CP_ACP);

  ►  마지막으로 제일 많이 보는 printf, scanf, etc

 이 모든 함수들은 ANSI 문자열을 씁니다. 이 함수들의 Unicode버전은 : wprintf, wscanf등이 되겠죠단순히 접두어 w만 쓰면., 또한 자주쓰는 strcpy의 wcscpy도 기억해두시길.

 WCHAR wchar[MAX] = “뽀하하”;

LPTSTR conv;

wcscpy(conv, wchar);

 중요한 사실은 유니코드를 다시 ANSI로 바꿀때는 코드 페이지에 대해 알아햐 한다는 사실을 기억해야 합니다.

특히 한글을 사용하는 우리에겐 코드 페이지라든지 민감한 사항이 많죠.

음.. 예를 들어 구문이라는 문자를 보겠습니다.

음. 제가 16진수로 뭐 이런거는 자세히 보지 않아서 쩝. -_-(사실 구차나서)

구(0xB1 0xB8)문(0xB9 0xAE) 이렇게 되니까

음. 대충 봐서 MultiByteToWideChar 같은 곳에서 페이징 변환 같은걸 안 해주면.

유니코드로 바뀔 때 요놈은 0x00 0xB1 0x00 0xB8 같이 되부릅니다. 1바이트씩 처리가.

하지만 제대로 알고 변환하면 0x….(유니코드 charset 에서 구에 해당하는게 되겠죠?)


음. 뒷이야기지만 제가 예전에 게시판을 보다가 본건데요. WinCE 에서 통신 프로그래밍시 CString 의 헛점을 보게 되었습니다.

그때에 것이 CString 은 Unicode 때에는 WideChar 로, 아니면 1바이트 char형으로 데이터를 가지게 되죠. 그래서 ce에서 작업시에 잘못 사용하면 큰 문제가…

그리고, WinCE 에서 TCHAR 형태를 유심히 봐야 합니다.

(나중에 시간나면 한번 자세히 설명을 하죠. 짭. 혼동만 올까봐.. 우선 이 정도로 언급만)


The TCHAR data type is a Win32 character string that can be used to describe ANSI,

DBCS, or Unicode strings. For ANSI and DBCS platforms, TCHAR is defined as follows:

typedef char TCHAR; 

For Unicode platforms, TCHAR is defined as synonymous with the WCHAR type.


자 MSDN 에서 보면 이놈은 상황에 따라 유니코드로도 적용이 됩니다.

하지만, CE 에서는 이 변환이 잘 안 되는듯. 아니면 -_-;

DeskTop 에서는 작동이 잘 되던게. 말로 보는게 아니라 직접 한번 해보시길. 꼭 해보세요.

그 외에도 버퍼 사이즈 구하기, MBCS등 이야기도 쫌 부족한 듯 하지만 앞으로 계속 나올 때 마다 보겠습니다.


치명적인 버그가 있군요..

 ► WideCharToMultiByte

WideCharToMultiByte는 여러 파라미터를 가지고 강력한 기능을 제공합니다.

이 함수는 모든 것을 조정할 수 있게 해줍니다: 코드 페이지, default character, ……

아주 중요하므로 익숙해지시길.

 char* GetAnsiString(const CString &s, UINT nCodePage)

{

int nSize = s.GetLength();

char *pAnsiString = new char[nSize+1];


WideCharToMultiByte(nCodePage, 0, s, nSize+1, pAnsiString, nSize+1, NULL, NULL);

return pAnsiString;

}

 CString strUnicode = _T("Some test string");

char *pAnsiString = GetAnsiString(strUnicode, CP_ACP);

 pAnsiString 를 nSize+1로 세팅 했는데, 이렇게 하면 버퍼가 부족하다는 에러를 냅니다.

사이즈를 와이드스트링의 길이가 아니라 바이트 수로 고쳐야 합니다.

'구문'은 ANSI 스트링으로 변환시 4바이트가 필요한데 와이드 스트링의 길이는 2밖에 안됩니다. 이를 다음과 같이 코딩합니다.

 char* GetAnsiString(const CString &s, UINT nCodePage)

{

int nSize =0;

char *pAnsiString =0;

int converted_count;

 // 어느 정도 크기의 버퍼가 필요한지 자동 계산...

nSize = WideCharToMultiByte(nCodePage, 0, s, -1, pAnsiString, nSize, NULL, NULL);

pAnsiString  = new char[nSize];

 converted_count = WideCharToMultiByte(nCodePage, 0, s, -1, pAnsiString, nSize, NULL, NULL);  


// Error

if ( converted_count == 0 )

{

        TRACE(L"문자열 변환에러");

        TRACE(GetLastError());

        return NULL;

   }

return pAnsiString;
}

 CString strUnicode = _T("Some test string");

char *pAnsiString = GetAnsiString(strUnicode, CP_ACP);

 그리고 wcstombs 를 사용한 부분도 잘못 코딩되어 있습니다.

다음과 같이 해야 합니다.

 // 코드페이지 지정하지 않고 컨버팅...

char *my_wcstombs( const WCHAR* wcs )

{

    int len;   

    char *mbs;

   

    len = wcstombs( NULL, wcs, wcslen(wcs)+1);

    mbs = new char[len];

    memset(mbs,0,len);


    if (!wcstombs( mbs, wcs, len+1 ))

{

        TRACE(L"문자열변환에러");

        return NULL;

}

    return mbs;

}