UTF-16

UTF-16 是 Unicode 字元編碼五層次模型的第三層：字元編碼表（Character Encoding Form，也稱為 "storage format"）的一種實現方式。即把 Unicode 字元集的抽象碼位對映為 16 位長的整數（即碼元）的序列，用於資料儲存或傳遞。Unicode 字元的碼位，需要 1 個或者 2 個 16 位長的碼元來表示，因此這是一個變長表示。

UTF 是 "Unicode/UCS Transformation Format" 的首字母縮寫，即把 Unicode 字元轉換為某種格式之意。UTF-16 正式定義於 ISO/IEC 10646-1 的附錄 C，而 RFC2781 也定義了相似的做法。

UTF-16 描述

Unicode 的編碼空間從 U+0000 到 U+10FFFF，共有 1,112,064 個碼位（code point）可用來對映字元。Unicode 的編碼空間可以劃分為 17 個平面（plane），每個平面包含 216（65,536）個碼位。17 個平面的碼位可表示為從 U+xx0000 到 U+xxFFFF，其中 xx 表示十六進位制值從 0016 到 1016，共計 17 個平面。第一個平面稱為基本多語言平面（Basic Multilingual Plane, BMP），或稱第零平面（Plane 0），其他平面稱為輔助平面（Supplementary Planes）。基本多語言平面內，從 U+D800 到 U+DFFF 之間的碼位區段是永久保留不對映到 Unicode 字元。UTF-16 就利用保留下來的 0xD800-0xDFFF 區段的碼位來對輔助平面的字元的碼位進行編碼。

1.1 從 U+0000 至 U+D7FF 以及從 U+E000 至 U+FFFF 的碼位

第一個 Unicode 平面（碼位從 U+0000 至 U+FFFF）包含了最常用的字元。該平面被稱為基本多語言平面，縮寫為 BMP（Basic Multilingual Plane，BMP）。UTF-16 與 UCS-2 編碼這個範圍內的碼位為 16 位元長的單個碼元，數值等價於對應的碼位。BMP 中的這些碼位是僅有的可以在 UCS-2 中表示的碼位。

1.2 從 U+10000 到 U+10FFFF 的碼位

輔助平面（Supplementary Planes）中的碼位，在 UTF-16 中被編碼為一對 16 位元長的碼元（即 32 位元，4 位元組），稱作代理對（Surrogate Pair），具體方法是：

碼位減去 0x10000，得到的值的範圍為 20 位元長的 0...0xFFFFF。
高位的 10 位元的值（值的範圍為 0...0x3FF）被加上 0xD800 得到第一個碼元或稱作高位代理（high surrogate），值的範圍是 0xD800...0xDBFF。由於高位代理比低位代理的值要小，所以為了避免混淆使用，Unicode 標準現在稱高位代理為前導代理（lead surrogates）。
低位的 10 位元的值（值的範圍也是 0...0x3FF）被加上 0xDC00 得到第二個碼元或稱作低位代理（low surrogate），現在值的範圍是 0xDC00...0xDFFF。由於低位代理比高位代理的值要大，所以為了避免混淆使用，Unicode 標準現在稱低位代理為後尾代理（trail surrogates）。

上述演算法可理解為：輔助平面中的碼位從 U+10000 到 U+10FFFF，共計 FFFFF 個，即 220=1,048,576 個，需要 20 位來表示。如果用兩個 16 位長的整陣列成的序列來表示，第一個整數（稱為前導代理）要容納上述 20 位的前 10 位，第二個整數（稱為後尾代理）容納上述 20 位的後 10 位。還要能根據 16 位整數的值直接判明屬於前導整數代理的值的範圍（210=1024)，還是後尾整數代理的值的範圍（也是 210=1024）。因此，需要在基本多語言平面中保留不對應於 Unicode 字元的 2048 個碼位，就足以容納前導代理與後尾代理所需要的編碼空間。這對於基本多語言平面總計 65536 個碼位來說，僅佔 3.125%。

由於前導代理、後尾代理、BMP 中的有效字元的碼位，三者互不重疊，搜尋是簡單的：一個字元編碼的一部分不可能與另一個字元編碼的不同部分相重疊。這意味著 UTF-16 是自同步（self-synchronizing）的：可以透過僅檢查一個碼元來判定給定字元的下一個字元的起始碼元。UTF-8 也有類似優點，但許多早期的編碼模式就不是這樣，必須從頭開始分析文字才能確定不同字元的碼元的邊界。

由於最常有的字元都在基本多文種平面中，許多軟體處理代理對的部分往往得不到充分的測試。這導致了一些長期的 bug 與潛在安全漏洞，它們甚至存在於廣為流行且評價頗高的應用軟體中。

1.3 從 U+D800 到 U+DFFF 的碼位

Unicode 標準規定 U+D800...U+DFFF 的值不對應於任何字元。

但是在使用 UCS-2 的時代，U+D800...U+DFFF 內的值被佔用，用於某些字元的對映。但只要不構成代理對，許多 UTF-16 編碼解碼還是能把這些不符合 Unicode 標準的字元對映正確的辨識、轉換成合規的碼元。按照 Unicode 標準，這種碼元序列本來應算作編碼錯誤。

1.4 範例：

以 U+10437 編碼（𐐷）為例:

0x10437 減去 0x10000，結果為 0x00437，二進位制為
分割它的上 10 位值和下 10 位值（使用二進位制）： 和
新增 0xD800 到上值，以形成：0xD800 + 0x0001 =
新增 0xDC00 到下值，以形成：0xDC00 + 0x0037 =
下表總結了一起示例的轉換過程，顏色指示碼點位如何分佈在所述的 UTF-16 中。由 UTF-16 編碼過程中加入附加位的以黑色顯示。

1.5 範例：UTF-16 編碼程式

假設要將 U+64321（16 進位）轉成 UTF-16 編碼。因為它超過 U+FFFF，所以他必須編譯成 32 位元（4 個 byte）的格式，如下所示：

V = 0x64321

Vx = V - 0x10000

= 0x54321

= 0101 0100 0011 0010 0001

Vh = 01 0101 0000 // Vx 的高位部份的 10 bits

Vl = 11 0010 0001 // Vx 的低位部份的 10 bits

w1 = 0xD800 // 結果的前 16 位元初始值

w2 = 0xDC00 // 結果的後 16 位元初始值

w1 = w1 | Vh

= 1101 1000 0000 0000

01 0101 0000

= 1101 1001 0101 0000

= 0xD950

w2 = w2 | Vl

= 1101 1100 0000 0000

11 0010 0001

= 1101 1111 0010 0001

= 0xDF21

所以這個字 U+64321 最後正確的 UTF-16 編碼應該是：

0xD950 0xDF21

而在小尾序中最後的編碼應該是：

0x50D9 0x21DF

因為這個字超過 U+FFFF 所以無法用 UCS-2 的格式編碼。

UTF-16 比起 UTF-8，好處在於大部分字元都以固定長度的位元組（2 位元組）儲存，但 UTF-16 卻無法相容於 ASCII 編碼。

UTF-16 的編碼模式

UTF-16 的大尾序和小尾序儲存形式都在用。一般來說，以 Macintosh 製作或儲存的文字使用大尾序格式，以 Microsoft 或 Linux 製作或儲存的文字使用小尾序格式。

為了弄清楚 UTF-16 檔案的大小尾序，在 UTF-16 檔案的開首，都會放置一個 U+FEFF 字元作為 Byte Order Mark（UTF-16 LE 以 FF FE 代表，UTF-16 BE 以 FE FF 代表），以顯示這個文字檔案是以 UTF-16 編碼，其中 U+FEFF 字元在 UNICODE 中代表的意義是 ZERO WIDTH NO-BREAK SPACE，顧名思義，它是個沒有寬度也沒有斷字的空白。

以下的例子有四個字元：「朱」（U+6731）、半形逗號（U+002C）、「聿」（U+807F）、「」（U+2A6A5）。

UTF-16 與 UCS-2 的關係

UTF-16 可看成是 UCS-2 的父集。在沒有輔助平面字元（surrogate code points）前，UTF-16 與 UCS-2 所指的是同一的意思。但當引入輔助平面字元後，就稱為 UTF-16 了。現在若有軟體聲稱自己支援 UCS-2 編碼，那其實是暗指它不能支援在 UTF-16 中超過 2 位元組的字集。對於小於 0x10000 的 UCS 碼，UTF-16 編碼就等於 UCS 碼。

3.1 Microsoft Windows 作業系統核心對 Unicode 的支援

Windows 作業系統核心中的字元表示為 UTF-16 小尾序，可以正確處理、顯示以 4 位元組儲存的字元。但是 Windows API 實際上僅能正確處理 UCS-2 字元，即僅以 2 位元組儲存的，碼位小於 U+FFFF 的 Unicode 字元。其根源是 Microsoft C++ 語言把 wchart 資料型別定義為 16 位元的 unsigned short，這就與一個 wchart 型變數對應一個寬字元、可以儲存一個 Unicode 字元的規定相矛盾。相反，Linux 平臺的 GCC 編譯器規定一個 wchar_t 是 4 位元組長度，可以儲存一個 UTF-32 字元，寧可浪費了很大的儲存空間。下例運行於 Windows 平臺的 C++ 程式可說明此點：

// 此原始檔在 Windows 平臺上必須儲存為 Unicode 格式（即 UTF-16 小尾）

// 因為包含的漢字 “𪚥”，不能在簡體中文版 Windows 預設的內碼表 936（即 GBK）中表示

// 該漢字在 UTF-16 小尾序中用 4 個位元組表示

// Windows 作業系統能正確顯示這樣的在 UTF-16 需用 4 位元組表示的字元

// 但是 Windows API 不能正確處理這樣的在 UTF-16 需用 4 位元組表示的字元，把它判定為 2 個 UCS-2 字元

include

int main

{

	const wchar_t lwc = L"𪚥";

	MessageBoxW (NULL, lwc, lwc, MB_OK);

	int i = wcslen (lwc);

	printf ("% d\n", i);

	int j = lstrlenW (lwc);

	printf ("% d\n", j);

	return 0;

}

Windows 9x 系統的 API 僅支援 ANSI 字元集，只支援部分的 UCS-2 轉換。1996 年釋出的 Windows NT 4.0 的 API 支援 UCS-2。Windows 2000 開始，Windows 系統 API 開始支援 UTF-16，並支援 Surrogate Pair；但許多系統控制元件比如文字框和 label 等還不支援 surrogate pair 表示的字元，會顯示成兩個字元。Windows 7 及更新的系統已經良好地支援了 UTF-16，包括 Surrogate Pair。

Windows API 支援在 UTF-16LE（wchart 型別）與 UTF-8（內碼表 CPUTF8）之間的轉碼。例如：

include

int main {char a1128, a2128 = { "Hello"};

	wchar_t w = L' 頁 ';

	int n1, n2= 5;

	wchar_t w1128;

	int m1 = 0;

	n1 = WideCharToMultiByte (CP_UTF8, 0, &w, 1, a1, 128, NULL, NULL);

	m1 = MultiByteToWideChar (CP_UTF8, 0, a2, n2, w1, 128);

}