Native/C++

L 리터럴과 유니코드 인코딩 (#pragma setlocale(

aucd29 2013. 10. 2. 19:03
http://includes.egloos.com/


c++에서 문자를 저장하기 위해 8 비트 크기의 char 타입 이외에 16 비트 크기(윈도우에서는 16 비트를 유닉스 계열에서는 32 비트를 사용한다고 합니다.)의 와이드 캐릭터를 저장하는 wchar_t 타입을 제공하고 있습니다. wchar_t 타입에는 문자의 MBCS/DBCS 및 UCSUTF-16(윈도우에서는 little endian을 기본적으로 사용합니다.) 인코딩을 저장할 수 있습니다. 또, wchar_t와 함께 와이드 캐릭터/문자열 상수를 지정하기 위해 "L"을 사용합니다.


문자열의 인코딩은 소프트웨어의 globalization 을 위해 필요한 여러 이슈중 하나로 MSDN 및 구글 검색을 통해 이 내용을 다루는 많은 웹 페이지를 검색해 볼 수 있습니다. 그 중 유니코드와 캐릭터 셋의 이해를 개발자의 필수항목으로 지적하고 있는 Joel Spolsky의 글을 보는 것이 도움이 될 듯합니다. 이 글은 얼마전 jrogue님이 번역한 "조엘 온 소프트웨어"를 통해 한글로도 읽어 볼 수 있습니다.
또 The Complete Guide to C++ Strings, Part I - Win32 Character Encodings 도 참고할 만한 문서입니다. 이 문서는 한글로도 번역되어 있습니다.([1], [2])



다음은 wchar_t와 유니코드를 다루면서 생각하지도 못했던 문제로 하루를 꼬박 보내버리게 된 이야기입니다.

문제는 wcslen() 메소드를 통해 L"" 로 지정된 문자열의 문자수를 알아오는데에서 시작했습니다.

setlocale(LC_ALL, ".949"); wchar_t *wstr1 = L"abc"; printf("length: %d", wcslen(wstr1)); --- 결과 --- length: 3
원하는 결과였습니다. 그런데 다음 코드의 결과는 이상합니다.

setlocale(LC_ALL, ".949"); wchar_t *wstr2 = L"가나다"; printf("length: %d", wcslen(wstr2)); --- 결과 --- length: 6
분명히 타입은 wchar_t 이므로 결과는 3이 나와야 할 것 같은데 char 타입과 strlen() 함수를 사용할 때와 마찬가지로 6이 나옵니다. 그래서 메모리를 덤프해봤습니다. L"abc" 의 경우 다음과 같이 USC-16 little endian 으로 정확히 저장되었습니다.
61 00 62 00 63 00 00 00
그런데 두번째 L"가나다" 의 경우
B0 00 A1 00 B3 00 AA 00 B4 00 D9 00 00 00(유니코드 한글 코드테이블과 KS-5601 코드테이블을 참고하세요)
"가나다"의 DBCS 인코딩을 단순히 16비트로 저장하고 있는 것이였습니다. 참고로 "가나다"의 DBCS 인코딩은 다음과 같습니다.
B0 A1 B3 AA B4 D9 00
혹시 지금 제가 사용 중인 영문 윈도우의 문제가 아닐 까 싶어, vmware를 통해 한글 윈도우 98 SE와 한글 윈도우 2000을 사용해 테스트해 봐도 같은 결과가 나오더군요.

이번에는 명시적으로 MBCS 문자열을 유니코드(USC-16 little endian)로 변환해 wchar_t에 저장해 보았습니다.
setlocale(LC_ALL, ".949"); char *str2 = "가나다"; wchar_t wstr2_uni[blocksize]; memset(wstr2_uni, 0, blocksize * sizeof(wchar_t)); mbstowcs(wstr2_uni, str2, _mbstrlen(str2)); --- wstr2_uni의 메모리 덤프 --- 00 AC 98 B0 E4 B2 00 00이렇게하니 처음에 원했던 바 데로 USC-16 little endian 으로 저장되더군요.


결론은

L""상수는 문자열을 유니코드로 인코딩하지 않는다. 다만 문자열을 16비트 wchar_t로 저장할 뿐이다. (이렇게 저장되어도 영문 Ascii는 유니코드 인코딩과 같은 결과를 얻을 수 있습니다.)

유니코드로 인코딩된 문자열을 얻기 위해서는 MBSC로 된 문자열을 유니코드로 변환해 사용해야 된다.



그건 그렇다고 치고, 아쉬운 점은 제가 본 유니코드 관련 문서들에서는 L"" 리터럴의 이와 같은 특성에 대한 내용은 찾아볼 수 없었다는 것입니다.
아마도 정작 유니코드의 사용이 필요한 언어권의 사람들이 쓴 것이 아닌 대부분 영문(혹은 라틴계열 문자)을 사용하는 사람들이 작성한 글이거나 이 글을 번역한 글이기 때문일 것이라 생각해 봅니다.

ps) 수십년간의 소프트웨어의 역사가 흘렀지만 아직도 globalization은 달성하기 힘든 높은 수준의 요구사항이 아닐까 합니다.

update
혹시나 하고 #pragma를 조사해보다 #pragma setlocale()를 발견했습니다. 다음과 같이 지정해줬더니 L"" 리터럴 문제가 모두 해결되는 군요.
#pragma setlocale(".949")//결국 컴파일 레벨에서 지정해주어야 한다.


좀 더 알아보고 포스트할 걸 그랬습니다.