본문 바로가기

Computer Science/Computer Systems

[Mini-Lecture] Character Sets and Unicode

#Character Sets

Character Set, 문자 집합이란, 컴퓨터에서 정보의 표현 수단으로 사용되는 글자들의 집합을 뜻한다. 문자 집합을 더 자세하게 알기 위해선 bytes가 무엇인지 알아야 한다. Byte는 디지털 정보 즉 데이터 처리의 기본 단위이다. 바이트는 1-bit가 8개, 즉 8비트이고, 옥텟으로 불리기도 한다. 바이트는 8비트 이기 때문에 2^8승 즉 256개의 값을 나타낼 수 있다. 이진법 에서는 0-255, 16진법 에서는 0x00 에서 0xff까지 나타낼수 있다. 문자랑 인간의 언어를 적는 데 사용하는 추상적이고 시각적인 기호 체계이다. 아래 예시를 한번 보자. 우리는 아무런 문자나 숫자 없이도 아래 그림들을 연결 지을수 있다 (사과, 나무, 해바라기, 프리첼, 비치볼, 집)

하지만 추상적이 문자열을 컴퓨터가 알아듣게 하기 위해선 숫자가 필요하다. 각 그림에 0 부터 5까지의 숫자를 부여해보자

0부터5까지의 정수를 사용하기때문에, 컴퓨터에서는 총 3비트가 필요하다. 이 인코딩에서 모든 문자는 3비트를 차지한다. 하지만 6과 7은 사용되지 않는다. 

각 2비트 묶음 하나 또는 두 개로도 인코딩을 할수 있다, 이 경우에는 사과, 나무, 해바라기는 2비트, 프리첼, 비치볼, 집은 4비트가 필요하다. 00 01 10 11 00 은 사과 나무 해바라기 프리첼 이라는 뜻이 된다. 

 

현실 세계 에서는 이러한 문자 집합을 나타낼 수 있게 도와주는 표준 부호들이 있다 (ASCII, ISO-8859-1, ISO-8859-2 등등). 또한 Unicode.

 

#Unicode

Unicode, 유니코드란 컴퓨터에서 세계 각국의 언어를 통일된 방법으로 표현할 수 있게 제안된 국제적인 문자 코드 규약이며 보편적인 문자 집합이다. Unicode 14.0 기준으로 144,697개의 codepoint로 불리는 문자들이 있고, 미래에는 1,114,112개의 code point/문자들을 수용할 수 있다. Code point는 U+0041 같은 스칼라 값으로 쓰여있다. 예를 들어 유니코드 문자 "A", U+0041은 라틴어 A를 나타내고 "Ä", U+00C4는 분음 부호표가 있는 라틴어 A가된다. U+1F385 는 🎅 산타 할아버지 이모티콘이다. 

 

이러한 문자 집합과 유니코드로 어떻게 프로그램 메모리에서 인코딩을 할까?

1,114,112개의 코드 포인트를 나타내기 위해선, 21 비트 (2^21 = 2, 097, 152)가 필요하다. 그래서 나온게 UTF-32 인코딩이다. UTF-32 인코딩은 각 문자마다 32비트를 부여하고 코드 포인트를(스칼라 값) 이용해 나타낸다. UTF-32는 n번째 문자가 a[n]에 있기 때문에 탐색에 용이하다. 그렇기 때문에 유니코드 문자열을 유니코드를 나열한 배열로 생각해도 된다. UTF-32에서 산타 할아버지 이모티콘은  🎅  0x0001F385로 나타낼 수 있다. Linux의 wchar_t도 32비트며 UTF-32 인코딩을 사용하고 C11의 문자 리터럴의 char32_t도 UTF-32 인코딩을 사용한다. 하지만 여기서 UTF-32 인코딩의 단점은 너무 낭비가 심하다는 것이다. 전에도 말했듯이 Unicode의  1,114,112 코드 포인트를 나타내기 위해선 21비트 밖이 필요하지 않다. 단순 영어만 사용하게 되면 ASCII에 비해 낭비가 4배 가까이 차이가 난다. 그렇기 때문에 낭비가 너무 심해 나온 인코딩이 UTF-16이다. 

 

UTF-16은 각 문자마다 16비트 즉 2바이트를 제공한다. 4bytes가 제공될 경우도 있는데 그 경우에 앞의 2바이트는 "Surrogate" 즉 대리라고 불리 운다. UTF-32와 달리 UTF-16에서의 산타 할아버지는 🎅 0xD83C 0xDF85 로 나타낼 수 있다. UTF-16도 UTF-32와 마찬가지로 배열같이 문자 탐색이 가능하다 (n 번째 문자:a[n]). 자바나 자바스크립트 같은 언어들이 UTF-16 인코딩을 사용한다 (Java의 경우 java.lang.Character를 사용해 UTF-16인코딩 식별가능). UTF-32 보다는 덜하지만 아직도 UTF-16 이 낭비인 상황도 많다. 그렇기 때문에 나온 것이 UTF-8 인코딩이다. 

 

UTF-8 인코딩은 variable-length (변수 길이) 인코딩으로 문자마다 1,2,3 또는 4 바이트를 문자마다 부여한다. UTF-8에서의 산타 할아버지는 🎅 0xF0 0x9F 0x8E 0x85로 나타낸다. 앞에 두 개의 인코딩과는 다르게 훨씬 효율적 일 때가 많다, 또한 7비트 ASCII 문자열들은 유효한 UTF-8 인코딩이다. 그렇게 때문에 낭비하는 메모리 공간도 없게 된다. 또한 최대 3자 이내의 입력 스트림과 동기화 가능하다. 하지만 UTF-8의 단점은 앞 두 개의 인코딩과는 다르게 indexing이 불가 하다는 점이다. 그럼에도 불구하고 현재 쓰이는 인코딩 중에서는 가장 흔한 인코딩 방법이다. Go lang과 Rust와 같은 언어들에서 쓰이고 있다.

Encoded Length and Value Bytes

#Q.바이트가 많이 주어지면 문자 집합의 인코딩을 나타내는 것인지 알 수 있을까?

 

아니다. 레거시 인코딩(예: ISO-8859-1)은 0 에서 255를 사용하여 256개의 가능한 값을 인코딩하는 단일 바이트 인코딩이다. 임의의 바이트 집합은 ISO-8859 문자 문자열의 유효한 인코딩이다. 모든 바이트 시퀀스가 유효한 UTF-8은 아니기 때문에 유효한 UTF-8이 아닌지는 알 수 있지만, 유효한 UTF-8처럼 보이는 것이 실제로 유니코드 문자의 인코딩이라는 보장은 없거나 알수있어도 우연인 경우가 많다. 따라서 파일 또는 객체의 문자 집합 인코딩에 대한 대역 외 정보를 수신하거나 가정하지 않는 한 파일 또는 전송된 객체를 안정적으로 해석할 수 없다. (HTTP 응답은: 내용 유형: 텍스트/문자, 문자 집합=UTF-8). 대부분의 파일 시스템(Linux, Windows)에 저장된 파일은 올바르게 해석될 수 있도록 프로그램을 열거나 사용자에게 의존한다. 일반적으로 "이 파일의 내용은 UTF-8 인코딩 유니코드로 해석된다."와 같은 정보를 기록하지 않는다. Suffixes들은 관습에 불과하다.

Encoding: value types to binary