문자셋, 인코딩, 그리고 프로그래밍과 관계된 것들.

|
프로그래밍을 하면서 상당수 부딪치는 부분이 "한글표현" 일 것이다.

주로 쓰는 많은 프로그래밍 언어나 환경들이 국외에서 만들어졌고,
한글/일어/중국어 같은 2-bytes 언어를 고려하지 않았던 탓인데, 그래도 요즘엔 많이 개선되었다.

프로그래밍을 하며 가장 많이 접하는 문자셋은 ASCII일 것이다.
0부터 127까지 각 바이트마다 문자 하나를 지정하여 문자를 표현하도록 한 문자셋이다.

이 영역에 숫자와 알파벳, 일부 특수기호들이 포함되어 있다.


프로그래밍에서는,
단순히 ascii 테이블로는 한글이 표현이 불가능하기 때문에
한글의 경우에는 2바이트를 사용하여 표현하게 된다. 이 값은 KSC-5601 국가표준코드에 정의되어 있다.
(자바 서블릿을 하다보면 한글 변환때문에 ISO-8859-1 문자셋을 보게 되는데, 이는 ASCII에 서유럽 언어를 포함시킨 것이다.)

이 문자셋을 기준으로 EUC-KR 인코딩이 사용되게 된다.


자, 그럼 여기서, 문자셋과 인코딩은 무슨 차이인가.
문자셋은 말 그대로 문자와 값을 1:1로 매칭시켜 놓은 테이블이다. 문자의 실제 값이라고 보면 된다.
인코딩은 문자셋을 저장하기 위한 표준이다.
아래에서 설명할 유니코드는 문자셋, UTF-8/UTF-16등은 인코딩이다.


어쨌든,
이 KSC-5601은 2350자를 테이블에 집어넣은 것인데,
한글로 표현이 가능한 범위는 2350자를 훨씬 더 넘어서기 때문에 슬슬 문제가 발생하기 시작한 것이다.

(이런 문제 -> http://news.naver.com/main/read.nhn?mode=LSD&mid=sec&sid1=102&oid=021&aid=0000071101 )

그리고 이 문자셋들은 국가마다 겹치는 코드 영역이 발생하게 되어 전세계적으로 문자셋을 제정하기에 이르렀다.
그렇게 탄생한 것이 유니코드이다.

유니코드 문자셋은 기본적으로 UCS2에 하나의 언어판이 포함되어 있으며,
UCS4에서는 128개의 언어판 그룹, 즉 128 * 256 = 32,768 언어판을 포함하게 된다.

32,768언어판이면 2의 31승 = 2,147,483,648자를 표현할 수 있다.

(문자셋에 할당된 각 문자는 유니코드 컨소시엄 사이트에서 볼 수 있다.
http://www.unicode.org/charts/
한글은 Hangul Syllables에 있고, 한자는 CJK Unified Ideographs 에 있다.
CJK는 Chinese-Japanese-Korean를 뜻한다. 한자는 이 3국에서 공통으로 사용하는 것이니까.)


흔히 보는 유니코드는 2바이트짜리 UCS2이다.


UCS는 문자셋이고 (Universal Charset Set) 이를 표현하기 위해 있는 인코딩 방식이 UTF(UCS Transformation Format) 이다.

UTF중에 가장 많이 보이는 것이 UTF-8인데 이 인코딩은 8비트를 기준으로 하게 된다.
ASCII범위 내의 값들은 1바이트를 이용하여 표현하고, 더 필요하면 2바이트, 더? 3바이트.... 이렇게 늘어나게 된다.
모든 문자가 같은 용량을 차지하는 것이 아니다.
(한글은 UTF-8로 표현시 한 글자가 3바이트를 차지하게 된다.)

UTF-8의 장점은 127이하의 값들은 ASCII와 호환이 가능한 점이다.



UTF-8이외에도 UTF-16, UTF-32등이 있지만 UTF-8이 많이 사용되는 추세이다.


그럼, 각 문자셋 간 변환은 가능한가?
이게 사실 프로그래밍을 하면서 이슈가 되는 점인데,
PC상이라면 OS에서 지원이 된다.

Win32상에서 프로그래밍을 할 때, 사용할 수 있는 함수가 MultiByteToWideChar와 WideCharToMultiByte이다.
(내가 알기로, 윈98부터는 내부적으로 유니코드를 사용하기때문에 프로그래밍 환경을 유니코드로 하는것이 낫다고 알고 있다.)

하지만 OS에서 지원이 되지 않는 경우에는 직접 변환을 해야 하는데,
UTF-8 / UTF-16등은 문자셋인 UCS로 동일하기 때문에 비트단위에서 바로 변환이 가능한데,

UTF-8에서 EUC-KR로 변환한다든가, 그 반대의 경우에는 단순한 계산법으로는 불가능하다.
요건 1:1로 매칭된 문자 테이블을 사용해야 한다.
EUC-KR의 경우 2350자 각각을 유니코드 값으로 저장하고 있는 데이터 (배열 등)이 있어야 한다는 거다.
(보통은 OS/Platform에서 지원한다.)

자바는 애초부터 유니코드를 사용했기 때문에 그나마 나은 편이고,
이 문자셋 문제는 C/C++에서 많이 나타나게 되는데, 이거에 대해 알아보자.

Visual Studio 6버전 이하에서는 프로젝트를 생성하게 되면 기본적으로 유니코드는 미지원 상태이다.
6버전은 그 전에 설치할때부터 유니코드 지원은 미선택 상태이다.
(예전에 퍼왔던 게시물. VC 6에서 유니코드 프로젝트 생성하기)

그리고 2003이후부터는 유니코드를 지원하게 되는데 이걸 잘 모르고 사용하는 사람이 많더라.

유니코드를 사용하게 되면서 보게 되는게 _T( ) 매크로와 L" " 인데,
L" "은 컴파일러에게 '이 문자열은 유니코드임 ㅇㅇ' 이라고 알려주는거다.
이 문자열을 저장하려면 기존에 사용하던 char이 아니라 wchar_t를 사용해야 한다. (WCHAR로 define되어 있다.)

_T( )매크로는 무엇이냐,
유니코드 프로젝트이면 L" "로 컴파일이 되고, 유니코드가 아니라면 char 문자열로 변환이 된다.

프로젝트를 유니코드로 생성하게 되면 프로젝트 환경 자체가 유니코드로 셋팅이 되고,
문자열을 받는 함수들은 유니코드로 문자열을 받게 된다.
(각 함수들은 ~A, ~W들로 작성이 되어 있고, 프로젝트 환경에 따라 호출할 함수가 결정되게 된다.
ex) MessageBox()는 MessageBoxA()와 MessageBoxW()이 따로 있다.)

만약 유니코드를 받는 함수에 대해 L이나 _T를 붙이지 않고 호출을 하게 되면
cannot convert parameter 1 from 'const char [17]' to 'wchar_t'
이라는 컴파일 에러가 나게 된다. (물론 17이라는 숫자는 문자열에 따라 바뀐다)



마지막으로,
만들어 쓰고 있는...
UTF8로 인코딩된 문자열 길이 구하는 함수...
[code c++]
UINTGetUTF8StringLength(BYTE * pByte)
{
 BYTE *pt = pByte;
 BYTE val = *pt;
 UINT nLen = 0;

 while(val != NULL) {  //UTF-8 문자열의 끝은 1-byte NULL
  if((val & 0xF0) == 0xF0) {  // 1111 0000 -> 4바이트 차지하는 문자
   pt += 4;
  }else if((val & 0xE0) == 0xE0) { // 1110 0000 -> 3바이트 차지하는 문자
   pt += 3;
  }else if((val & 0xC0) == 0xC0) { // 1100 0000 -> 2바이트 차지하는 문자
   pt += 2;
  }else if((val & 0x80) == 0x80) { // 그 외인데 첫번째 비트가 1인 경우는 없음. 포인터만 넘김.
   pt++;
  }else {  // ASCII범위의 문자열. 한바이트만 차지.
   pt++;
  }
  val = *pt;

  nLen += 1;
 }

 return nLen;
}

[/code]


ps. 알고 있는 지식으로 작성했으므로 틀린점이 있을수도 있습니다. 틀린점이 있으면 알려주시기 바랍니다

And