UTF-16
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
UTF-16은 유니코드 문자를 인코딩하는 방식 중 하나로, 각 유니코드 코드 포인트를 1개 또는 2개의 16비트 코드 유닛으로 표현한다. 1980년대 후반 범용 문자 집합 개발 과정에서 시작되었으며, 16비트 기반의 UCS-2 인코딩의 한계를 극복하기 위해 서로게이트 쌍을 도입하여 확장되었다. UTF-16은 기본 다국어 평면(BMP)의 문자는 1개의 코드 유닛으로, BMP 외부의 문자는 2개의 코드 유닛(서로게이트 쌍)으로 인코딩한다. UTF-16은 윈도우, 자바 등 다양한 운영체제 및 프로그래밍 언어에서 사용되며, UTF-8, UTF-32, Shift_JIS 등 다른 인코딩 방식과 비교하여 장단점을 가진다.
더 읽어볼만한 페이지
- 유니코드 변환 형식 - UTF-8
UTF-8은 유니코드 문자를 표현하는 가변 길이 문자 인코딩 방식으로, ASCII 코드와 호환성을 유지하며 다양한 언어의 문자를 표현할 수 있도록 설계되었지만, 보안 문제점과 공간 효율성 측면에서 단점을 가진다. - 유니코드 변환 형식 - UTF-1
UTF-1은 유니코드 초기 버전을 인코딩하기 위해 1992년에 설계된 가변 길이 문자 인코딩 방식으로, ASCII 호환성을 유지하고 ISO 2022 및 MIME과의 호환성을 고려했지만, "모듈로 190" 산술을 사용하는 특징과 현대 유니코드 표준과의 차이점을 가진다. - 유니코드에 관한 - UTF-8
UTF-8은 유니코드 문자를 표현하는 가변 길이 문자 인코딩 방식으로, ASCII 코드와 호환성을 유지하며 다양한 언어의 문자를 표현할 수 있도록 설계되었지만, 보안 문제점과 공간 효율성 측면에서 단점을 가진다. - 유니코드에 관한 - UTF-1
UTF-1은 유니코드 초기 버전을 인코딩하기 위해 1992년에 설계된 가변 길이 문자 인코딩 방식으로, ASCII 호환성을 유지하고 ISO 2022 및 MIME과의 호환성을 고려했지만, "모듈로 190" 산술을 사용하는 특징과 현대 유니코드 표준과의 차이점을 가진다. - 문자 인코딩 - 유니코드
유니코드는 세계의 모든 문자를 하나의 컴퓨터 인코딩 표준으로 통합하기 위해 설계되었으며, 유니코드 컨소시엄에 의해 관리되고 UTF-8, UTF-16, UTF-32 등의 부호화 형식을 제공하지만, 일부 문자 표현 문제, 버전 간 비호환성, 레거시 인코딩과의 호환성 문제 등의 과제를 안고 있다. - 문자 인코딩 - UTF-8
UTF-8은 유니코드 문자를 표현하는 가변 길이 문자 인코딩 방식으로, ASCII 코드와 호환성을 유지하며 다양한 언어의 문자를 표현할 수 있도록 설계되었지만, 보안 문제점과 공간 효율성 측면에서 단점을 가진다.
| UTF-16 | |
|---|---|
| 일반 정보 | |
![]() | |
| 종류 | 유니코드 변환 형식(UTF), 가변 너비 인코딩 |
| 포함 언어 | 국제 |
| 인코딩 대상 | ISO/IEC 10646(유니코드) |
| 기반 | UCS-2 |
| 설명 | |
| 특징 | 1개 또는 2개의 16비트 코드 유닛을 사용하여 유니코드를 가변 길이로 인코딩 |
| 호환성 | ASCII와 호환되지 않음 |
| 기타 | |
| 관련 표준 | 유니코드 표준 |
2. 역사
1980년대 후반, 기존 언어별 인코딩을 하나의 통일된 시스템으로 대체할 "범용 문자 집합"(UCS) 개발 작업이 시작되었다. 목표는 전 세계 대부분 언어의 문자와 과학, 수학, 음악 등 기술 분야의 기호를 포함하는 것이었다. 초기에는 문자당 2바이트(16비트)를 사용하는 65,536(216) 값의 인코딩(UCS-2)을 사용했다.[1][2][12]
UTF-16은 유니코드 문자 인코딩 방식 중 하나로, 대부분의 문자를 16비트(2바이트)로 표현한다. 각 유니코드 코드 포인트는 1개 또는 2개의 16비트 코드 유닛으로 인코딩된다.[42]
ISO/IEC JTC 1/SC 2와 유니코드 컨소시엄이 이 작업을 병행했으며, 개발 중인 인코딩이 서로 호환되도록 문자 할당을 동기화했다. 초기 2바이트 인코딩은 "유니코드"라고 불렸지만, 현재는 "UCS-2"라고 불린다.[1][2][12]
그러나 216 문자가 충분하지 않다는 것이 명확해지면서,[13] IEEE는 더 큰 31비트 공간과 문자당 4바이트를 필요로 하는 인코딩(UCS-4)을 도입했다. 유니코드 컨소시엄은 문자당 4바이트가 많은 메모리와 디스크 공간을 낭비하고, 일부 제조업체가 이미 문자당 2바이트 기술에 투자했다는 이유로 반대했다. UTF-16 인코딩 방식은 이러한 타협의 결과로 개발되었으며, 1996년 7월 유니코드 표준 2.0 버전과 함께 도입되었다.[14] 2000년 IETF에서 발행한 RFC 2781에 명시되면서 국제 표준으로 자리잡았다.[15][16]
UTF-16은 국제 표준 ISO/IEC 10646 및 유니코드 표준의 최신 버전에 명시되어 있다. "UCS-2는 이제 구식으로 간주되어야 합니다. 이는 더 이상 10646 또는 유니코드 표준의 인코딩 형식을 나타내지 않습니다."[1][2] UTF-16은 더 많은 코드 포인트를 지원하거나 대리자에 의해 대체된 코드 포인트를 지원하도록 확장되지 않을 것이다.[17]
3. 설명
기본 다국어 평면(BMP, U+0000 ~ U+FFFF)에 속하는 문자들은 하나의 16비트 코드 유닛으로 표현되며, 이 코드 유닛은 코드 포인트의 숫자 값과 동일하다. 예를 들어, 로마자 소문자 'z' (U+007A)는 0x007A로, 한자 '水' (U+6C34)는 0x6C34로 인코딩된다.
BMP 외부에 있는 문자들(U+10000 ~ U+10FFFF)은 서로게이트 쌍(Surrogate Pair)이라는 두 개의 16비트 코드 유닛을 사용하여 표현된다.
다음은 UTF-16 인코딩의 예시이다.인코딩 바이트 순서 바이트 열 UTF-16LE 리틀 엔디언 34 6C, 7A 00, 34 D8 1E DD UTF-16BE 빅 엔디언 6C 34, 00 7A, D8 34 DD 1E UTF-16 리틀 엔디언, with BOM FF FE, 34 6C, 7A 00, 34 D8 1E DD UTF-16 빅 엔디언, with BOM FE FF, 6C 34, 00 7A, D8 34 DD 1E
UTF-16으로 인코딩된 문자는 16비트 단위로 처리되므로, 파일 입출력이나 통신 시에는 바이트 순서(Byte Order)를 고려해야 한다. UTF-16BE는 빅 엔디언, UTF-16LE는 리틀 엔디언 방식을 사용하며, UTF-16은 바이트 순서 표지(BOM)를 통해 엔디언을 명시하거나, BOM이 없는 경우 빅 엔디언으로 처리한다.[42]
3. 1. 서로게이트 쌍 (Surrogate Pair)
BMP 외부의 코드 포인트를 표현하기 위해 사용되는 특수한 코드 단위 쌍이다. 상위 서로게이트(High Surrogate, U+D800 ~ U+DBFF)와 하위 서로게이트(Low Surrogate, U+DC00 ~ U+DFFF)로 구성된다.[18]
코드 포인트에서 0x10000을 뺀 후, 그 값을 상위 10비트와 하위 10비트로 나눈다. 상위 10비트는 0xD800에 더해져 상위 서로게이트가 되고, 하위 10비트는 0xDC00에 더해져 하위 서로게이트가 된다.[19]
시각적으로 표현하면 다음과 같다:
```text
U' = yyyyyyyyyyxxxxxxxxxx // U - 0x10000
W1 = 110110yyyyyyyyyy // 0xD800 + yyyyyyyyyy
W2 = 110111xxxxxxxxxx // 0xDC00 + xxxxxxxxxx
```
서로게이트 쌍은 UTF-16에서 자기 동기화되는 특징을 가진다. 즉, 코드 유닛의 값 범위를 확인하면 해당 유닛이 문자의 시작인지 아닌지를 알 수 있어, 문자열 중간에서 시작해도 문자의 경계를 쉽게 찾을 수 있다. UTF-8도 이러한 장점을 공유한다.
하지만, 가장 많이 사용되는 문자가 모두 BMP에 있기 때문에, 서로게이트 쌍 처리가 제대로 테스트되지 않는 경우가 많다. 이는 잘 알려진 응용 소프트웨어에서도 버그나 보안 취약점으로 이어질 수 있다.
| 스칼라 값 | UTF-16 | 비고 |
|---|---|---|
| `xxxxxxxxxxxxxxxx` | `xxxxxxxxxxxxxxxx` | |
| `000uuuuuyyyyyyxxxxxxxxxx` | `110110wwwwyyyyyy 110111xxxxxxxxxx` | `wwww = uuuuu - 1` |
대리 코드 위치(Surrogate Code Point)라고 불리는 `U+D800`–`U+DFFF` 범위는 BMP 밖의 문자를 표현하는 서로게이트 쌍에 사용된다. 이 영역은 UTF-16에서만 사용되며, UTF-8, UTF-32에서는 사용되지 않는다. 유니코드 코드 위치의 최대값이 U+10FFFF인 이유는 UTF-16으로 표현할 수 있는 최대값이기 때문이다.
3. 2. U+D800 ~ U+DFFF (서로게이트)
유니코드 ''코드 포인트''는 1개 또는 2개의 16비트 ''코드 유닛''으로 인코딩된다. 216 이상인 코드 포인트("BMP 외부")는 ''두 개''의 16비트 코드 유닛을 사용하여 인코딩되는데, 이 두 개의 16비트 코드 유닛은 이전에 문자에 할당되지 않은 UTF-16 대리 범위 (0xD800–0xDFFF)에서 선택된다.[18] 이 범위의 값은 문자로 사용되지 않으며, UTF-16은 이를 개별 코드 포인트로 코딩할 수 있는 합법적인 방법을 제공하지 않는다.다른 평면의 코드 포인트는 ''서로게이트 쌍''이라고 하는 두 개의 16비트 ''코드 유닛''으로 인코딩된다. 첫 번째 코드 유닛은 ''상위 서로게이트''(0xD800–0xDBFF), 두 번째는 ''하위 서로게이트''(0xDC00–0xDFFF)이다.
서로게이트 쌍을 생성하는 방법은 다음과 같다.
- 0x10000을 코드 포인트 ''(U)''에서 빼서 16진수 범위 0x00000–0xFFFFF의 20비트 숫자 ''(U')''를 만든다.
- 상위 10비트(범위 0x000–0x3FF)는 0xD800에 더해져 ''상위 서로게이트'' ''(W1)''를 생성한다.
- 하위 10비트(범위 0x000–0x3FF)는 0xDC00에 더해져 ''하위 서로게이트'' ''(W2)''를 생성한다.
''상위 서로게이트''(0xD800–0xDBFF)와 ''하위 서로게이트''(0xDC00–0xDFFF)는 상호 배타적이므로, 서로게이트가 BMP 문자와 일치하거나 인접한 두 개의 ''코드 유닛''이 유효한 ''서로게이트 쌍''처럼 보이는 것은 불가능하다.
UTF-16에서 `U+D800`–`U+DFFF` 의 코드 위치를 대리 코드 위치(Surrogate Code Point)라고 부르며, BMP 밖의 하나의 코드 위치를 나타내는 연속된 두 개의 대리 코드 위치 쌍을 서로게이트 쌍이라고 부른다. 대리 코드 위치는 UTF-16에서만 사용되기 때문에 BMP의 이 영역에는 문자가 할당되어 있지 않으며, UTF-16 이외의 UTF-8, UTF-32에서는 사용되지 않는다.[42]
공식 유니코드 표준은 UTF-16을 포함한 어떤 UTF 형식도 서로게이트 코드 포인트를 인코딩할 수 없다고 명시하고 있다. 이러한 코드 포인트는 문자로 할당되지 않으므로 인코딩할 이유가 없다. 그러나 윈도우는 파일 이름[20] 및 기타 위치에서 페어가 없는 서로게이트(Unpaired Surrogate)를 허용하며, 이는 일반적으로 유니코드 표준에서 제외되었음에도 불구하고 소프트웨어에서 지원해야 함을 의미한다.
4. 인코딩 방식
UTF-16은 유니코드 문자열을 인코딩하는 방식 중 하나로, 각 문자를 16비트 코드 단위로 나타낸다. 1980년대 후반, 기존의 언어별 인코딩을 통합하여 전 세계의 문자와 기호를 표현하기 위한 범용 코드화 문자 집합(UCS) 개발 작업이 시작되었다. 초기에는 문자당 2바이트(16비트)를 사용하는 UCS-2 방식이 사용되었으나, 216개의 문자로는 충분하지 않다는 것이 명확해졌다.[1][2][12][13]
IEEE에서 더 큰 31비트 공간을 사용하는 UCS-4 인코딩을 제안했지만, 유니코드 컨소시엄은 메모리와 디스크 공간 낭비, 기존 투자 등의 이유로 반대했다. 그 결과, 1996년 7월 유니코드 표준 2.0 버전에서 UTF-16 인코딩 방식이 타협안으로 도입되었다.[14] UTF-16은 2000년 IETF에서 발행한 RFC 2781에 명시되어 있다.[15][16]
UTF-16에서, 대리 코드 위치를 제외한 코드 위치(유니코드 스칼라 값)는 16비트 부호 없는 정수를 부호 단위로 하는 부호 단위열로 나타낸다. 부호 단위열은 하나 또는 두 개의 부호 단위(16비트 또는 32비트)로 구성된다.
기본 다국어 평면(BMP)에 속하는 문자들(U+0000–U+D7FF, U+E000–U+FFFF)은 그대로 16비트 값으로 표현된다. BMP에 속하지 않는 문자들(U+10000–U+10FFFF)은 다음과 같이 두 개의 16비트 코드 단위(서로게이트 쌍)로 표현된다.
| 스칼라 값 | UTF-16 | 비고 |
|---|---|---|
xxxxxxxxxxxxxxxx | xxxxxxxxxxxxxxxx | |
000uuuuuyyyyyyxxxxxxxxxx | 110110wwwwyyyyyy 110111xxxxxxxxxx | wwww = uuuuu - 1 |
여기서 U+D800–U+DFFF 범위의 코드 위치는 서로게이트 쌍을 위해 예약된 대리 코드 위치이며, 이 영역에는 문자가 할당되지 않는다. UTF-16 이외의 UTF-8, UTF-32에서는 이 대리 코드 위치가 사용되지 않는다. 유니코드 코드 위치의 최댓값(U+10FFFF)은 UTF-16으로 표현할 수 있는 최대값이다.
UTF-16 인코딩 형식으로 표현된 문자는 정보 교환을 위해 파일 읽기/쓰기나 통신 시 적절한 방식으로 바이트 직렬화되어야 한다. 인코딩 스킴에는 UTF-16, UTF-16BE, UTF-16LE 세 종류가 있다. UTF-16BE는 16비트 정수를 빅 엔디언으로, UTF-16LE는 리틀 엔디언으로 직렬화한다. UTF-16의 경우에는 바이트 순서 표지(BOM)로 엔디언을 명시하거나, 상위 프로토콜로 지정되지 않고 BOM도 부착하지 않는 경우에는 빅 엔디언으로 하도록 정해져 있다.[42]
| 인코딩 | 바이트 순서 | 바이트 열 |
|---|---|---|
| UTF-16LE | 리틀 엔디언 | 34 6C, 7A 00, 34 D8 1E DD |
| UTF-16BE | 빅 엔디언 | 6C 34, 00 7A, D8 34 DD 1E |
| UTF-16 | 리틀 엔디언, with BOM | FF FE, 34 6C, 7A 00, 34 D8 1E DD |
| UTF-16 | 빅 엔디언, with BOM | FE FF, 6C 34, 00 7A, D8 34 DD 1E |
4. 1. 바이트 순서 (Byte Order)
UTF-16은 16비트 코드 유닛의 시퀀스를 생성한다. 각 유닛은 2개의 8비트 바이트로 구성되므로, 바이트 순서는 컴퓨터 아키텍처의 엔디안(바이트 순서)에 따라 달라질 수 있다.코드 유닛의 바이트 순서를 인식하는 데 도움을 주기 위해, '''UTF-16'''은 U+FEFF 값을 갖는 바이트 순서 표시(BOM)를 코드 값 앞에 허용한다. (U+FEFF는 보이지 않는 너비가 없는 공백(ZWNBSP) 문자이다.) 디코더의 엔디안이 인코더와 일치하면 0xFEFF 값을 감지하지만, 반대 엔디안 디코더는 BOM을 U+FFFE로 해석한다.
BOM이 없는 경우, RFC 2781은 빅 엔디안(BE) 인코딩을 가정하도록 권장한다.[42] 그러나 실제로는 Windows가 기본적으로 리틀 엔디안(LE) 순서를 사용하기 때문에, 많은 응용 프로그램이 리틀 엔디안 인코딩을 가정한다.
표준은 '''UTF-16BE''' 또는 '''UTF-16LE'''를 지정하여 바이트 순서를 명시적으로 지정할 수 있도록 허용한다. 이 경우 BOM이 텍스트 앞에 붙지 않도록 되어 있으며, 시작 부분의 U+FEFF는 ZWNBSP 문자로 처리해야 한다.
인터넷 프로토콜의 경우 IANA는 "UTF-16", "UTF-16BE", "UTF-16LE"를 인코딩 이름으로 승인했다.
다음은 UTF-16으로 인코드된 예시이다.
| 인코딩 | 바이트 순서 | 바이트 열 |
|---|---|---|
| UTF-16LE | 리틀 엔디언 | 34 6C, 7A 00, 34 D8 1E DD |
| UTF-16BE | 빅 엔디언 | 6C 34, 00 7A, D8 34 DD 1E |
| UTF-16 | 리틀 엔디언, with BOM | FF FE, 34 6C, 7A 00, 34 D8 1E DD |
| UTF-16 | 빅 엔디언, with BOM | FE FF, 6C 34, 00 7A, D8 34 DD 1E |
4. 2. UTF-16BE, UTF-16LE
UTF-16BE는 16비트 정수를 빅 엔디언으로 직렬화하며, UTF-16LE는 리틀 엔디언으로 직렬화한다. UTF-16BE와 UTF-16LE에서는 바이트 순서 표지(BOM)를 붙이지 않으며, U+FEFF는 ZWNBSP(Zero Width Non-Breaking Space, 너비가 없는 띄어쓰기) 문자로 처리한다.[42]| 인코딩 | 바이트 순서 | 바이트 열 |
|---|---|---|
| UTF-16LE | 리틀 엔디언 | 34 6C, 7A 00, 34 D8 1E DD |
| UTF-16BE | 빅 엔디언 | 6C 34, 00 7A, D8 34 DD 1E |
5. 예시
(UTF-16으로 인코드된 물 수, z, 높은음자리표)
