본문 바로가기

프로그래밍/C++

ANSI와 UTF-8과의 상호 변환

ANSI에 대해

http://ko.wikipedia.org/wiki/EUC-KR

위의 링크 내용을 요약하면
CP949 = 현대 한글 + EUC-KR
EUC_KR = KS X 1001 + KS X 1003
KS X 1001 = 한글 및 한자 모음
KS X 1003 = ASCII + '\(원 문자)'
정도 되겠음다.

유니코드에 대해

http://b.mytears.org/2005/01/101

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줄이면 충분하더군요.
나름 쓸모있을 내용같아서 정리해서 포스팅합니다.

참고

유니코드에 관한 설명
http://b.mytears.org/2005/01/101

EUC-KR, CP949 에 관한 위키피데아
http://ko.wikipedia.org/wiki/EUC-KR

API를 이용하는 유니코드와 ANSI 문자열간의 변환 방법
http://www.winapiprogramming.com/MyHome/api.htm#2 (링크 깨짐)

  1. Windows API에서는 2 Bytes, Linux에서는 4 Bytes [본문으로]
  2. stania군 제보로 수정, stania 감사! [본문으로]