오전 10:45 2007-06-19
SBSC MBSC Unicode 정리
조경민 bro@shinbiro.com
=======================================================================


참고
--------------------------------------------------------
1. Tong - tigglet님의 STL/C++통
http://tong.nate.com/tigglet/35577610


2. The Complete Guide to C++ Strings, Part I - Win32 Character Encodings
By Michael Dunn.
http://www.codeproject.com/string/cppstringguide1.asp


3. The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About


Unicode and Character Sets (No Excuses!) By Joel Spolsky
http://www.joelonsoftware.com/articles/Unicode.html


4. Character Sets MSDN
http://msdn2.microsoft.com/en-us/library/ms776430.aspx


5. UTF-8 history
http://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt


6. UTF-8 RFC3629
http://www.ietf.org/rfc/rfc3629.txt


7. UNICODE PRIVATE FAQ 0.1 (2004.08.03) by redfrog@jitco.net
http://blog.naver.com/guruby?Redirect=Log&logNo=140004680346


8. Unicode Code Converter v4
http://people.w3.org/rishida/scripts/uniview/conversion.php


http://www.w3c.or.kr/i18n/www-i18n/


http://www.copyeditor.co.kr/reference/char-font/johabwansung.htm


http://blog.naver.com/photo1448?Redirect=Log&logNo=30003683825


http://sparcs.kaist.ac.kr/~jwjung/seminar/hangul-i18n/ko-code.html


http://trade.cbnu.edu/%7Eleesl/code/isounicode.html#1


http://blog.naver.com/k4722?Redirect=Log&logNo=140024341569 (KSC5601)


http://kmh.ync.ac.kr/unix/linuxFAQ/code/


KSSM: 조합형 KSSM 삼보컴퓨터
KSC-5601: 완성형 코드 2232자 (국제표준 ISO-2022, ISO/IEC-10646 호환)
KSC-5657: (국제표준 ISO-2022, ISO/IEC-10646 호환)
EUC-KR: 확장 유닉스코드 변형, 영어는 KSC-5636, 한글은 KSC-5601 사용
MS 통합 완성형(확장 완성형, Unified Hangule, Codepage 949): KSC-5601 호환



character set과 encording
--------------------------------------------------------
character set은 문자 집합, 각 문자당 숫자를 부여한 체계
encording은 컴퓨터상에서 해당 문자를 표현한 상태의 문자집합
예를 들어 완성형한글 KSC5601 codeset (character set)은
Unix에서 euc-kr encording으로 표현되고, Dos에서는 codepage 949로
인코딩되어 표현된다. 윈도우는 기본으로 한글 코드는 완성형 949이다.



1. Single-Byte Character Sets(SBSC) 각 문자는 하나의 바이트
--------------------------------------------------------
한바이트에 총 255개의 문자를 표현 가능 한문자료 표현 가능한 다른 나라말(히브리어, 프랑스


어)등은 code page를 바꾸어서 글짜를 표현.
strlen()등 함수 사용. 마지막 문자는 0.


1.1 ASCII (ISO-8859-1)
ASCII는 7비트(0~127)를 이용하여 영어를 표현 (0x00부터 0x7F)


1.2 OEM Character Sets
IBM PC는 ASCII 한바이트의 안쓰는 상위 비트로 특수 문자(0x80부터 0xFE)를 표현
이 때 코드페이지를 활용해서 한바이트에 총 255개의 문자를 표현 가능
한문자료 표현 가능한 다른 나라말(히브리어, 프랑스어)등은 code page를
바꾸어서 글짜를 표현.
2바이트가 필요한 한국어 같은 아시아 문자는 표현 불가 따라서 MBCS필요.


2. MultiByte Character Sets
--------------------------------------------------------
각 문자가 여러 바이트로 표현되는 문자셋
윈도우에서는 MBCS의 일종으로 DBCS가 있음.
SBSC를 여러바이트로 확장하였고 codepage는 동일하게 존재함.
한글 KSC5601 체계를 위한 codepage 949


2. 1 Double-Byte Character Sets
--------------------------------------------------------
다른 말로 expanded 8-bit character set. SBSC에서 코드페이지를 확장시켜
기존 SBSC에서 중국어 한국어등을 표현하기 위해서 설계됨.
숫자나 영어는 1바이트로, 중국어 한국어 일본어 같은 문자는 2바이트를 하나의 문자로 표현.
윈도우 VC에서는 _MBCS 매크로로 되어 있다.


char s[] = "ABC가나다";
char* p = s;
int codepage = GetACP(); 현재 코드페이지 949 ANSI/OEM - Korean (Unified Hangeul Code)
int len = strlen(s);    // 9 리턴
int len2 = _mbslen((unsigned char*)s); // 6 리턴
while( *p )
{
   p = CharNext( p ); // ABC는 1바이트 전진, 가나다는 2바이트씩 전진
   or
   if( IsDBSCLeadByte(*p ) // 두바이트 문자 선두 바이트면
      p += 2;
   else
      p ++;
}
41 42 43 B0 A1 B3 AA B4 D9 00
A  B  C    가    나    다 


문자열 연산시 strchr이 아닌 _mbschr 등을 써야 함.
마지막 문자는 0 한 바이트.


3. 유니코드
-------------------------------------------------------------
윈도우는 일반적으로 UTF-16을 사용
email이나 xml에서 encording시 UTF-8을 쓸 때가 많음


3.1 UTF-8
UTF-8은 영어 같이 값이 0~126이하(Unicode < 127 )이면 한바이트로 처리하는 방식이다.
윈도우는 기본으로 UTF-16으로 wchar_t가 2바이트를 차지한다.


3.1 UTF-16 or UCS-2
일반적으로 두 바이트를 한 문자로 처리, (영어든 숫자든, 한국어, 중국어)
이렇게 두 바이트를 한 문자로 처리하는 걸 UTF-16또는 UCS-2라고 표현
문자열 연산시 _wcslen()등을 써야 함. 마지막 문자는 00 00 두 바이트임.


wchar_t *p = L"Hello";


Hello 는 다음 과 같음.
U+0048 U+0065 U+006C U+006C U+006F.
메모리 상에
48 00 65 00 6C 00 6C 00 6F 00 00 00
  H     e     l     l     o    EOS


VC에서는 UNICODE(WinAPI용) 과 _UNICODE (표준 c 함수들용)를 함께 정의해야함.



기타


A1. 윈도우 문자셋 지원
----------------------------------------
Win9x는 DBCS로 처리, 유니코드 지원 안함.
WinNT는 유니코드로 처리, DBCS 지원



A2. DBCS에서 문자열 처리 시 주의 사항
----------------------------------------


1. 문자열 이동시 ++, -- 사용 금지
   => CharNext(), CharPrev() 사용


2. 문자열 계산 시 strlen 사용 금지
   => strlen("ABC한글") => 7
      따라서 _mbslen("ABC한글") => 5 해야 알맞게 계산 가능
   => 또는 strchr("ABC한글",'/') 같은거 하면 문자열내에 일본어나
      한국어 중국어에 /와 Ascii코드값이 같은 문자가 포함되어 있을 수 있다.
      따라서 _mbschr("ABC한글",'/') 해야 한다.


3. 문자열 내 위치 계산 시
   => 함부로 pNextChar = pStr + 4; 이렇게 생각하면 안된다.



A3. 유니코드(UTF16) <--> DBCS
----------------------------------------
MultiByteToWideChar()와 WideCharToMultiByte()를 사용


A4. 유니코드(UTF16) <--> UTF8
----------------------------------------
   int WideCharToMultiByte(
      UINT CodePage,            // code page
      DWORD dwFlags,            // performance and mapping flags
      LPCWSTR lpWideCharStr,    // wide-character string
      int cchWideChar,          // number of chars in string
      LPSTR lpMultiByteStr,     // buffer for new string
      int cbMultiByte,          // size of buffer
      LPCSTR lpDefaultChar,     // default for unmappable chars
      LPBOOL lpUsedDefaultChar  // set when default char used
    );


이 함수의 첫번째 매개변수 CodePage에 CP_UTF8를 할당하여 사용합니다.



A5. L""과 한글
----------------------------------------
L""안에 한글을 넣으면 L은 그냥 한글을 분리하여 short으로 만드므로
원하는 결과가 나오지 않는다.


setlocale(LC_ALL, ".949");
wchar_t *wstr2 = L"가나다";
printf("length: %d", wcslen(wstr2)); // 6이 리턴됨.


--- 결과 ---
length: 6
출처 : Tong - tigglet님의 STL/C++통
B0 00 A1 00 B3 00 AA 00 B4 00 D9 00 00 00


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
출처 : Tong - tigglet님의 STL/C++통



A6. UTF-8
------------------------------------------
MSN 메신저 개발이나, email content 문자열 처리 등을 하려면 UTF-8 처리 해야한다.


UTF-8은 영어 같이 값이 0~126이하(Unicode < 127 )이면 한바이트로 처리하는 방식이다.
Unicode < 127 0xxxxxxx
Unicode 128 ~ 255 110000xx 10xxxxxx
이렇게 해서 아래처럼 공식화 된다.


   Char. number range  |        UTF-8 octet sequence
      (hexadecimal)    |              (binary)
   --------------------+---------------------------------------------
   0000 0000-0000 007F | 0xxxxxxx
   0000 0080-0000 07FF | 110xxxxx 10xxxxxx
   0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
   0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx



   The character sequence U+0041 U+2262 U+0391 U+002E "A   TO>." is encoded in UTF-8 as follows:


       --+--------+-----+--
       41 E2 89 A2 CE 91 2E
       --+--------+-----+--


   한국어 U+D55C U+AD6D U+C5B4 is encoded in UTF-8 as follows:
       --------+--------+--------
       ED 95 9C EA B5 AD EC 96 B4
       --------+--------+--------
        
   The character sequence U+65E5 U+672C U+8A9E (Japanese "nihongo",
   meaning "the Japanese language") is encoded in UTF-8 as follows:
  
       --------+--------+--------
       E6 97 A5 E6 9C AC E8 AA 9E
       --------+--------+--------


A7. UTF-16 유니코드 <--> 조합형 한글 처리
------------------------------------------------------
초성 19개, 중성21개, 종성28개(원래 27개 이지만 종성이 없는 경우까지 해서 28개가 된다.)로 총 글자수는 19X21X28 = 11172로서 유니코드에서 지원하는 글자 수와 일치한다.
유니코드에서는 완성된 한글 글자외에도 한글초성, 중성,종성을 따로 정의하고 있다.


자모값을 포함한 하나의 스트링을 받아 한글로 변환하는 방법을 유니코드 버전 2에서 제시하고


있다. 초성, 중성, 종성의 각 인덱스값을 각각 Cho_i, Jung_i, Jong_i로 정의한다면 유니코드


글자값(V)은 다음과 같이 구할 수 있다.


V = { [(Cho_i × 21) + Jung_i] × 28 } + Jong_i + 0xAC00


void IndexToUnicode (unsigned short Cho_i, Jung_i, Jong_i)
{
   unicodeValue = ((Cho_i * 21 + Jung_i)) * 28;
   if(Jong_i)
   {
    unicodeValue += Jong_i + 1;
   }
   unicodeValue += 0xAC00;
}
예:0x1111=ㅍ, 0x1171=ㅟ, 0x11B6=ㅀ {((0 × 21)+0) × 28 + 0 + 0xAC00} = 0xAC00



void toJohab(unsigned short Cho_i, Jung_i, Jong_i)
{
   Cho_i += 2;
   Jung_i += 3;
   if(Jung_i >= 25) Jung+=2;
     /* 중성 인덱스 24, 25는 유니코드에 정의되지 않았음 */
   if(Jung_i >= 18) Jung+=2;
     /* 중성 인덱스 16, 17은 유니코드에 정의되지 않았음 */
   if(Jung_i >= 9)  Jung+=2;
     /* 중성 인덱스 8, 9는 유니코드에 정의되지 않았음 */
   Jong_i += 2;
 
   johabValue = (1 << 15)|(Cho_i << 10)|(Jung_i << 5)|Jong_i;
}


A8. UTF-16 유니코드 초성 중성 종성 분리 코드
-------------------------------------------------------
#include "stdafx.h"
#include
#include
 


    //초성
static const wchar_t wcHead[] = {
           L'ㄱ', L'ㄲ', L'ㄴ', L'ㄷ',          
           L'ㄸ', L'ㄹ', L'ㅁ', L'ㅂ',
           L'ㅃ', L'ㅅ', L'ㅆ', L'ㅇ',
           L'ㅈ', L'ㅉ', L'ㅊ', L'ㅋ',
           L'ㅌ', L'ㅍ', L'ㅎ'};
 
    //중성
static const wchar_t wcMid[] = {
          L'ㅏ', L'ㅐ', L'ㅑ', L'ㅒ',
          L'ㅓ', L'ㅔ', L'ㅕ', L'ㅖ',
          L'ㅗ', L'ㅘ', L'ㅙ', L'ㅚ',
          L'ㅛ', L'ㅜ', L'ㅝ', L'ㅞ',
          L'ㅟ', L'ㅠ', L'ㅡ', L'ㅢ', L'ㅣ'};
 
    //종성
static const wchar_t wcTail[] = {
           L' ', L'ㄱ', L'ㄲ', L'ㄳ',
           L'ㄴ', L'ㄵ', L'ㄶ', L'ㄷ',
           L'ㄹ', L'ㄺ', L'ㄻ', L'ㄼ',
           L'ㄽ', L'ㄾ', L'ㄿ', L'ㅀ',
           L'ㅁ', L'ㅂ', L'ㅄ', L'ㅅ',
           L'ㅆ', L'ㅇ', L'ㅈ', L'ㅊ',
           L'ㅋ', L'ㅌ', L'ㅍ', L'ㅎ'};
 
int BreakHan(const wchar_t *str, wchar_t *buffer, UINT nSize)
{
    UINT    pos = 0;
 
    while(*str != '\0')
    {
        if(*str < 256)
        {
            if(pos+2 >= nSize-1) break;
 
            buffer[pos] = *str;
            ++pos;
        }
        else
        {
            if(pos+4 >= nSize-1) break;
 
            buffer[pos]   = wcHead[(*str - 0xAC00) / (21*28)];
            buffer[pos+1] = wcMid[(*str - 0xAC00) % (21 * 28) / 28];
            buffer[pos+2] = wcTail[(*str - 0xAC00) % 28];
 
            pos+=3;
        }
 
        ++str;
    }
 
    buffer[pos] = '\0';
    return pos;
}
 
int GetIndex(int wcType, wchar_t ch)
{
    int Length;
    const wchar_t *pArray;
    switch(wcType) // 초성,중성,종성 배열 : 1,2,3
    {
        case 1 : Length=19;
                 pArray = wcHead;
                 break;
        case 2 : Length=21;
                 pArray = wcMid;
                 break;
        case 3 : Length=28;
                 pArray = wcTail;
                 break;
    }
 
    int k=0;
    for(; k    {
        if( *pArray==ch ) return k;
    }
   
    return -1; // Not Exist
}
 
inline wchar_t ToHan(wchar_t ch1, wchar_t ch2, wchar_t ch3)
{
    int idx1 = GetIndex(1, ch1);
    int idx2 = GetIndex(2, ch2);
    int idx3 = GetIndex(3, ch3);
   
    return (idx1*21*28 + idx2*28 + idx3 + 0xAC00);
}
 
void MergeHan(const wchar_t *buffer, wchar_t *han)
{  
    while(*buffer)
    {
        if(*buffer<256) // 1바이트문자
        {
            *han++ = *buffer++;
        }
        else    // 2바이트 한글.
        {
            if(GetIndex(2, buffer[3])==-1 && buffer[2]>=256) // buffer[3]이 중성(모음)이


아니고
            {                                                // 종성이 2바이트 문자면.. 
                *han = ToHan( buffer[0], buffer[1], buffer[2] );
                ++han;
                buffer+=3;
            }
            else // 모음이면 종성으로 ' '보냄
            {
                *han = ToHan( buffer[0], buffer[1], L' ' );
                ++han;
                buffer+=2;
            }
        }
    }
    *han=0;
}
 
#define printf TRACE
int Test_main()
{
    wchar_t *str = L"나는 한AB국CD을 사랑합니다...";
    wchar_t buffer[4096];
 
    setlocale(LC_ALL, "Korean");  // DBSC 완성형 한글 코드 페이지 선택
 
    printf("원본문자열 :%S\n", str);
 
    BreakHan(str, buffer, sizeof buffer);    
    printf("분리문자열 :%S\n", buffer);
 
 
    wchar_t recover[1024];
    MergeHan(buffer, recover);
    printf("복원문자열 :%S\n", recover);
 
 
    wchar_t test1[] = L"ㄱㅏㄴㅡㄴ ㅅㅔㅇㅝㄹ ㄱㅡ ㄴㅜㄱㅏ";
    wchar_t test2[] = L"ㄱㅏaㄴㅡㄴ ㅅㅔabcㅇㅝㄹ ㄱㅡ  ㄴㅜㄱㅏ"; 
 
    MergeHan(test1, recover);
    printf("복원문자열 :%S\n", recover);
   
    MergeHan(test2, recover);
    printf("복원문자열 :%S\n", recover);
 
 
    return 0;
}


원본문자열 :나는 한AB국CD을 사랑합니다...
분리문자열 :ㄴㅏ ㄴㅡㄴ ㅎㅏㄴABㄱㅜㄱCDㅇㅡㄹ ㅅㅏ ㄹㅏㅇㅎㅏㅂㄴㅣ ㄷㅏ ...
복원문자열 :나 는 한AB국CD을 사 랑합니 다 ...
복원문자열 :가는 세월 그 누가
복원문자열 :가a는 세abc월 그  누가


A9. DBCS나 MBCS에서 두바이트 이상 문자는 bigendian에서도 같나?
-----------------------------------------------------------------------------------------------


두바이트가 하나의 문자를 표현할때, big endian cpu에서도 little때 처럼
첫번째 바이트를 Lead 바이트로 보고 다음 바이트를 Tail 바이트로 처리
하면 된다.
즉, 두바이트라고 해서 short 값으로 표현하는게 아니다. char ch[2]인것이다.

'KB > MFC/Win32' 카테고리의 다른 글

Windbg 덤프 분석  (0) 2008.01.14
FAT Filesystem Long File Name 처리  (0) 2007.06.19
PE 헤더 정보  (0) 2006.06.16
Design and Implementation of Power-Aware Virtual Memory  (0) 2006.06.12
IGlobalInterfaceTable  (0) 2006.03.24

+ Recent posts