Programming,  컴퓨터와 인터넷

CP949(UHC)를 UCS4로 안전하게 변환하는 C++ 기법

#include 
#include 
#include 

using namespace std;
typedef __enc_traits enc_type;
typedef codecvt unicode_codecvt;
typedef codecvt_base::result result;
typedef wchar_t int_type;
typedef char ext_type;
typedef __gnu_cxx::char_traits int_traits;

int main(void)
{
locale loc(locale::classic(), new unicode_codecvt);
if (!has_facet(loc)) {
cerr << "don't have facet" << endl;
return -1;
}
const unicode_codecvt* cvt = &use_facet(loc);
unicode_codecvt::state_type state01("UCS4", "UHC");
if (state01._M_good() == false) {
cerr << "can't make state" << endl;
return -1;
}
state01._M_init();

const ext_type* e_lit = "쒝 슌 꽌";
const ext_type* e_lit2 = "시냇물 처졸 냇";
const ext_type* efrom_next;
int_type* i_arr = new int_type[1024];
int_type* ito_next;
result r1;

int size = strlen(e_lit);
int size2 = strlen(e_lit2);

r1 = cvt->in(state01, e_lit, e_lit + size, efrom_next,
i_arr, i_arr + size, ito_next);
if (r1 != codecvt_base::ok)
return -1;
for (int i = 0; i < size && i_arr[i]; ++i) {
if (i_arr[i] == 0x20000000) {
printf("\n");
} else {
printf("0x%08X\n", i_arr[i]);
}
}
cout << endl;

r1 = cvt->in(state01, e_lit2, e_lit2 + size2, efrom_next,
i_arr, i_arr + size2, ito_next);
if (r1 != codecvt_base::ok)
return -1;
for (int i = 0; i < size2 && i_arr[i]; ++i) {
if (i_arr[i] == 0x20000000) {
printf("\n");
} else {
printf("0x%08X\n", i_arr[i]);
}
}
cout << endl;

return 0;
}

실행 결과는 다음과 같다. 각각의 글자가 제대로 된 UCS4 워드값을 가지고 있음을 볼 수 있다.

0x9DC40000 // 쒝

0x8CC20000 // 슌

0x4CAF0000 // 꽌

0xDCC20000 // 시
0xC7B00000 // 냇
0x3CBB0000 // 물

0x98CC0000 // 처
0x78C80000 // 졸

0xC7B00000 // 냇

codecvt 대신에 mbstowcs()를 사용하게 되면 완성형 문자집합의 범위를 넘어서는 확장완성형 코드의 경우 '?'로 변환되어 '쒝', '슌', '꽌' 등의 글자가 모두 같은 워드값을 가지게 되는 문제점이 나타난다.

다만 위 소스코드의 한 가지 아쉬운 점은 locale 설정, codecvt 생성, state 생성을 초기화하는 별개 함수로 분리할 수 없다는 점이다. state를 포인터나 레퍼런스로 넘기게 되면 cvt의 in() 메쏘드를 호출할 때 내부에서 segmentation fault가 발생한다. 아직 해결책을 찾지 못하고 같은 함수에서 사용하는 방법을 사용하고 있다.

* 2009/08/20에 추가
GCC 4.3.3에서 테스트해보니 위의 예제가 header dependency 문제로 인하여 제대로 컴파일되지 않는 문제가 있어서 GCC 4.3.3에서 컴파일되는 다른 예제를 작성해보았다.

이 예제는 UTF-8 문자열을 codecvt를 이용하여 UCS4로 변환하는 기능을 수행한다. 물론 이 기능은 mbstowcs() 함수를 이용하여 아주 간단하게 작성할 수도 있으나 codecvt를 사용하는 복잡한 코드를 이해할 수 있는 예제로 소개한다.

#include 
#include 
#include 
#include 
#include 
#include 
#include 


using namespace std;

typedef encoding_state state_type;
typedef codecvt unicode_codecvt;


int main(void)
{
locale loc(locale::classic(), new unicode_codecvt);
if (!has_facet(loc)) {
cerr << "don't havve facet" << endl;
return -1;
}
const unicode_codecvt* cvt = &use_facet(loc);
unicode_codecvt::state_type state01("UCS4", "UTF8");

const char* text = "한글";
const char* efrom_next;
wchar_t* i_arr = new wchar_t[1024];
wchar_t* ito_next;
codecvt_base::result r1;

int size = strlen(text);
r1 = cvt->in(state01, text, text + size, efrom_next,
i_arr, i_arr + size, ito_next);
if (r1 != codecvt_base::ok)
return -1;

for (uint i = 0; i_arr[i] != 0x00000000 ; ++i) {
if (i_arr[i] == 0x20000000) {
cout << endl;
} else {
cout << "0x" << hex << htonl(i_arr[i]) << dec << endl;
}
}
cout << endl;

return 0;
}

애초의 목적이었던 CP949를 UCS4로 변화하는 것은 이 글을 읽은 독자의 몫으로 남겨둔다. 위의 두 가지 예제를 참고하면 아주 간단하다.

댓글 한 개

답글 남기기