또 엄청나게 삽질 -.- 이번에는 ANSI <-> UTF-8 문제가 아니라, WideByte 쪽으로 고생 했다
ASP 에서 인자값으로 C++로 넘기는 부분이였는데 VARIANT 타입의 변수를 다루는 것에 뭐 이런게 다있나 하면서 해메고 -_- 그 변수에서의 스트링이 나는 ANSI 나 유니코드로 들어올 줄 알았고 혼자서 삽질을했다
계속 삽질하다가 나중에 char 포인터로 한 바이트씩 디버그해보니까 '<' + '\0' + 'x' + '\0' + 'm' + '\0' + 'l' + '\0' + ...... WideByte 타입 이였었다 -ㅁ-
결국은 답은 간단: ANSI 코드로 바꿀 경우에는 W2A() 를 쓰고, 유니코드로 바꿀 경우에는 WideCharToMultiByte() 를 쓰면 되더라ㅋ
오랫만에 C++ . . 어렵더라 역시나 공부 좀 할껄 그랬어.. (원래 촘 못한다;) 음 공부 한 것은.. VARIANT 타입에 대해서 조금은 공부했고, (글구 SAFEARRAY 도) ASP가 보통의 경우에 유니코드, ANSI 이런거 안쓰고 Wide Character 를 쓴다는거 ? ?
Unicode는 기본적으로 Byte
order mark 후에 해당하는 글자들이 나오게됩니다. 즉 UTF-8의 경우( 아래 표 참조 ) EF BB BF EA B0 80 EA B0 80 이렇게 적혀 있으면
가가 라고 보이게 됩니다. 단, UTF-8에 한해서
Byte order mark가 없어도 상관없습니다.
Encoding
UTF-8
UTF-16BE
UTF-16LE
UTF-32BE
UTF-32LE
‘가’
EA B0 80
AC 00
00 AC
00 00 AC 00
00 AC 00 00
Smallest code point
0000
0000
0000
0000
0000
Largest code point
10FFFF
10FFFF
10FFFF
10FFFF
10FFFF
Code unit size
8 bits
16 bits
16 bits
32 bits
32 bits
Byte order
N/A
big-endian
little-endian
big-endian
little-endian
Byte order mark
EF BB BF
FE FF
FF FE
00 00 FE FF
FF FE 00 00
Minimal bytes
1
2
2
4
4
Maximal bytes
6
4
4
4
4
위에 Code unit size에도
나와있듯 Unicode는 무조건 2바이트 혹은 4바이트인 것은 아닙니다. 1바이트가 아닌 문자는 Wide
Character라고 하죠.
즉, Unicode는 C++에서 Wide Character를 나타내는 wchar_t와는 그
개념이 아주 다릅니다. wchar_t는 char처럼 단순히 일정한 크기1 일 뿐이고 이것으로 표현되는 것은 UCS-2 혹은 UCS-4와 같은 고정바이트 형 문자인
것이죠. 따라서 최소 단위가 1 Byte인 UTF-8은 char로 취급해야만 합니다. 그렇지 않으면 제대로 처리되지
않아요.2
ANSI와 UTF-8의 상호 변환
리눅스에서는 iconv라는
함수를 제공합니다. man iconv_open 을 치면 상세한
설명이 나와있습니다. ( 남자는 man. 그 후 스타죠. ) 콘솔창에서는 iconv -f UTF-8 -t CP949 -o cp949.txt
utf-8.txt 이런 식으로 쓰면 됩니다. 다만 UTF-8에서 다른 포맷으로 변경시 Byte order mark를 인식하지
못합니다. 즉, 파일의 처음이 EF BB BF로 시작하면 iconv로는 다른 포맷으로 변경이 되지 않습니다. 안타깝지만 파일의 처음을
열어서 Byte order mark가 있으면 제거해주는 루틴이 필요합니다. 파일 인코딩 포맷을 알 수 있는 명령어로는 file <file> 이 있습니다. 이를 이용하면 utf8.txt: UTF-8 Unicode text, with no line
terminators 와 같이 떠요. UTF-8의 Byte order mark를 인식 못하는 것은 iconv뿐이고
나머지에서는 잘 인식됩니다.
윈도우에서는 MultiByteToWideChar()를 제공합니다. 유사 포맷으로는 mbstowcs()가 있지만 이 함수는 UTF-8은 제공하지 않습니다. ANSI
코드에서 UTF-8로 가기 위해서는 다음과 같은 변환과정을 거쳐야 합니다. ANSI( MutiByte ) -> UCS-2( WideByte ) -> UTF-8( MultiByte ) 대략 코드로 보면 다음과 같습니다.
void
foo( const char *in, char *out, int nOut ) { USES_CONVERSION;
wchar_t *wc = A2W( in ); // ANSI to UCS-2 WideCharToMultiByte( CP_UTF8,
0, wc, -1, out, nOut, 0, 0 ); // UCS-2 to UTF-8 }
참고로 A2W나
USES_CONVERSION은 ATL 관련 함수입니다. 반대로 변환할 떄는 MultiByteToWideChar()와 W2A()를 이용하시면 됩니다. 위에도 적어놨지만 ANSI나 UTF-8이나 다
MultiByte입니다. A2W()는 내부적으로 MultiByteToWideChar()를, W2A()는 내부적으로
WideCharToMultiByte()를 호출합니다. 매크로를 쓴 건 예제 길이를 짧게 하고 싶어서 쓴 거니 너무 신경쓰진
마세요.
이거 찾는다고 Managed Extension까지 갔다오고 벼라별 짓을 다 했는데.. 결국 저 3줄이면
충분하더군요. 나름 쓸모있을 내용같아서 정리해서 포스팅합니다.