인코딩과 캐릭터셋

예전에 인코딩과 캐릭터셋; encoding and character set 이란 주제로 페이지를 작성하고 있었는데, 쓰기 시작하고 얼마 안지나서 대학원 일이 늘어나 쓰기를 관뒀던 주제를 다시 써보려고 한다.

굳이 프로그래밍 언어를 딱 골라잡아서 얘기하지 않아도, 현재 주류에 해당하는 언어들에는 – 몇몇 예외가 있긴하지만 – 다국어를 표현하기 위한 개념이나 요소들이 조금씩 들어가 있다. 현재 주류 언어 중에 가장 오래되었다고 / 그리고 가장 기본적이라고 할 수 있는 C언어의 경우에는 특정 ‘문자’를 저장하는 단위로 두 가지 타입을 사용하고 있다.

  • 우선 char 라는 1 byte의 문자를 저장하기 위한 타입이 있다.
  • 두번째로 wchar_t 라는 확장된 문자를 저장하기 위한 타입이 있다1.

가장 먼저 기억해야하는 사실은 이게 “저장"하기 위한 것이지 문자들을 “표현"하기 위한 것이 아니란 것이다.

각 문자는 _어떤 숫자 값_으로 나타내어진다 – 이것이 캐릭터 셋; character set 이다. 그리고 이렇게 나타내어진 숫자 값을 어떤 비트 패턴으로 변형하는 것 – 이게 우리가 실제로 저장하고 해석하는 것들이다 – 이 바로 인코딩; encoding 이다.

문제는 보통 사용되는 문자에 대한 데이터 타입이 char 인데, char는 1byte의 크기를 갖고 대부분의 경우 1byte = 8bits 이기 때문에, 기껏해야 256개의 숫자표현만 저장할 수 있다. 그래서 한글 혹은 여러 나라의 말들을 표현하려면, 그래서 2개, 3개 혹은 그 이상의 char 데이터타입을 연이어서 사용하게 된다.2

예를 들어 우리가 Windows code page 949 혹은 UNIX 혹은 그 variant들에서 사용하는 euc-kr (둘이 완전히 같진 않지만 거의 같다고 봐도)의 경우에는 ‘ABC한글’ 이라는 문자열을 다음과 같이 char 데이터 덩어리로 쪼개서 표현하게 된다. (편의상 LE/16진수 표현을 사용한다)

ABC한글 : 0x (41) (42) (43) (c7 d1) (b1 db) (괄호 단위로 한 글자씩에 해당한다)

A, B, C는 각각 0x41, 0x42, 0x43 이라는 숫자값으로 표현되는데 euc-kr 표현에서는 이를 앞 뒤에 뭘 붙이지 않고 그대로 bit 표현으로 옮긴다. 한글의 경우에는 조금 복잡한 방법으로 표현하게 된다 – 숫자값 128 이상인 지점들을 쓰긴하지만 주요 제어 문자들이 중간에 끼지않게 세심하게 조절해서 2bytes로 표현한다. 그래서 일반적인 ASCII 문자(0~127)들의 경우 1 byte를, 그 이상의 경우 2 bytes까지도 사용하는 경우가 있다.

이런 방법 말고도 UCS2; UTF-16라는 표현방법도 존재한다. Windows 2000 이상에서는 내부적으로 이 방식을 사용하는데 – 파일시스템이라거나 레지스트리 등에 전부 이 것을 쓴다 – 이 방법은 모든 문자를 특정 순서(Unicode의 BMP)로 배치하고, 이 것을 무조건 2 bytes에 저장하는 형식을 띈다. 예를 들어 앞에서 사용한 ‘ABC한글’의 경우 다음과 같이 표현하게 된다.

ABC한글: 0x (41 00) (42 00) (43 00) (5c d5) (00 ae) (괄호 단위로 한 글자씩에 해당한다, 여기선 litten endian을 썼다.)

이 표현의 경우엔 흔히 1 bytes로만도 표현되는 영문자들도 동일하게 2 bytes를 사용하게 된다. 그래서 이 경우엔 밑바닥에서 저장하는 타입으로 흔히 2bytes 크기로 사용되는 wchar_t를 사용한다.

즉 여기까지의 내용을 정리해보면, 어떤 문자열이란 것은 크게 세가지 요소로 구성된다.

  1. 해당하는 언어에서 각각의 문자들을 숫자로 표현하는 방법: 캐릭터셋; character set
  2. 숫자를 어떤 비트 패턴으로 바꾸는 방법: 인코딩; encoding
  3. 이런 비트 패턴을 저장하는 데이터 타입: C/C++에서의 char, wchar_t

  1. 조금 생각해보면 발견할 수 있는 사실인데 데이터 형이 unsigned char가 아니다. 원래 처음 C를 만들 때에는 0~127번까지 – 7bits 으로 표현 가능한 – ASCII 문자셋을 저장할 용도였다. 그리고 크기는 언제나 1 byte인 것이다; 물론 1byte가 8 bits 이란 보장도 없(었)다. 덤으로, wchar_t의 크기도 명시되어 있지 않다. 다만 해당하는 구현에서 확장된 문자집합의 제일 큰 코드를 표현할 정도는 되야한다 정도로 정의되어있다. (from ISO C Amendment 1) ↩︎

  2. 사실 char를 여러개 붙여서 쓰므로 “데이터 타입이 char다” 라고 말하는 것은 조금 이상하지만 나중에 다시 신경써서 다시 다루기로 한다. ↩︎