Unicode中UTF-8與UTF-16編碼詳解

黃Java發表於2018-04-11

概述

本文通過介紹Unicode編碼以及對應的兩種編碼方式UTF-8和UTF-16,讓讀者能夠了解關於字串編碼的相關知識,同時能夠弄清楚Unicode和UTF-8和UTF-16之間的關係。

本文的主要內容為:

  • Unicode編碼,包含Unicode編碼基礎知識以及與UTF-8和UTF-16這兩種編碼方式的關係
  • UTF-8編碼,包含基礎概念和Unicode編碼轉換到UTF-8編碼方式
  • UTF-16編碼,包含基礎概念和Unicode編碼轉換到UTF-16編碼方式
  • JavaScript中string與DOMString

本文作為utfx.js原始碼解析的基礎知識儲備文章,通過了解UTF-8和UTF-16這兩種編碼方式,讀者能夠理解使用JavaScript進行編碼轉換的原理。

如果想了解編碼轉換的使用場景,可以閱讀我之前的部落格WebSocket系列之JavaScript字串如何與二進位制資料間進行互相轉換

如果想了解utfx.js相關的原始碼內容,可以關注我的後續文章。

Unicode編碼

概念

Unicode(統一碼、萬國碼、單一碼)是電腦科學領域裡的一項業界標準,包括字符集、編碼方案等。Unicode 是為了解決傳統的字元編碼方案的侷限而產生的,它為每種語言中的每個字元設定了統一併且唯一的二進位制編碼,以滿足跨語言、跨平臺進行文字轉換、處理的要求。1990年開始研發,1994年正式公佈。

通常Unicode編碼是通過2 Byte來表示一個字元的,如U+A12B,2 Byte的二進位制表示方法結果就是1010(A)0001(1) 0010(2)1011(B)

簡單介紹完了Unicode,我們來看下UTF-8和UTF-16。需要注意的是:UTF是Unicode TransferFormat的縮寫,UTF-8和UTF-16都是把Unicode碼轉換成程式資料的一種編碼方式。

UTF-8

概念

UTF-8(8-bit Unicode Transformation Format)是一種針對Unicode的可變長度字元編碼,又稱萬國碼。由Ken Thompson於1992年建立。現在已經標準化為RFC 3629。UTF-8用1到6個位元組編碼Unicode字元。用在網頁上可以統一頁面顯示中文簡體繁體及其它語言(如英文,日文,韓文)。

通過上面的介紹我們可以知道,UTF-8是一種非常通用的可變長字元編碼方式。

首先,我們來介紹下什麼叫做可變長編碼?可變長編碼就是指在針對某個字元進行編碼時,他的表示長度是不固定的。像UTF-8裡面,ASCII所表示的字符集就是用1 Byte來表示,而大部分漢字則是用3 Byte來表示。

相較於Unicode統一使用2 Byte來表示字元,在遇到大部分字元都可以用1 Byte表示時,能夠節省許多儲存空間。但是,如果遇到需要用超過2 Byte來表示的字元,那麼UTF-8的編碼方式則會消耗更多的儲存空間。

表示方式

通過上面的介紹我們可以知道,不同的Unicode碼在UTF-8中佔用了不同的儲存空間。下面我們就通過一個表格來看下將Unicode字元轉換為UTF-8編碼方式的具體步驟。其中的?表示轉換成UTF-8編碼後,Unicode碼佔用的二進位制位置。

Unicode碼範圍 UTF-8編碼方式
U+0000~U+007F 0????????
U+0080~U+07FF 110????? 10??????
U+0800~U+FFFF 1110???? 10?????? 10??????
U+10000~U+10FFFF 11110??? 10?????? 10?????? 10??????

當我們得到Unicode碼後,我們先根據上面的這個表判斷其所處的範圍,然後將Unicode碼轉換為二進位制表示,從後往前擷取UTF-8編碼中所留為之長度,從前往後依次填入對應位置,所即可得到UTF-8的編碼。我們舉兩個例子來看下:

  • U+0020,這個字元的小於0000 007F,所以只需要用1 Byte來進行編碼。U+0020的二進位制表示為0000(0)0000(0) 0010(2)0000(0),那麼從後往前擷取7位得到010 0000,放入UTF-8編碼方式中,得到的結果為00101111,轉換為十六進位制得到2F。因此儲存在記憶體中的的順序就是2F
  • U+A12B,這個字元大於0000 0800,小於0000 FFFF,因此需要用3 Byte來進行編碼。U+A12B的二進位制表示為1010(A)0001(1) 0010(2)1011(B)。,那麼從後往前擷取16位得到10100001 00101011(Unicode碼本身),放入UTF-8編碼中,得到的結果為11101010 10000100 10101011,轉換十六進位制得到EA84AB。因此,儲存在記憶體中的順序就是EA 84 AB

通過上面的例子,我相信大家對UTF-8的編碼有了一個深入的理解。下面,讓我們來看下另一種編碼方式——UTF-16。

UTF-16

概念

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

引用維基百科中對於UTF-16編碼的解釋我們可以知道,UTF-16最少也會用2 Byte來表示一個字元,因此沒有辦法相容ASCII編碼(ASCII編碼使用1 Byte來進行儲存)。

表示方式

在UTF-16中,我們將Unicode分為了兩個範圍,分別通過不同的方式進行儲存。具體表示見下圖。

Unicode範圍 UTF-16編碼方式
U+000~U+FFFF 2 Byte儲存,編碼後等於Unicode值
U+10000~U+10FFFF 4 Byte儲存,現將Unicode值減去(0x10000),得到20bit長的值。再將Unicode分為高10位和低10位。UTF-16編碼的高位是2 Byte,高10位Unicode範圍為0-0x3FF,將Unicode值加上0XD800,得到高位代理(或稱為前導代理,儲存高位);低位也是2 Byte,低十位Unicode範圍一樣為0~0x3FF,將Unicode值加上0xDC00,得到低位代理(或稱為後尾代理,儲存低位)

根據上面的轉換方式,我們就能夠將Unicode碼根據UTF-16的編碼方式進行轉換。下面我們仍然通過兩個例子來看下:

  • U+0020,這個值的範圍在第一部分,即經過UTF-16編碼後,結果仍然為U+0020,在記憶體中的順序為00 20
  • U+12345, 這個值的範圍在第二部分,因此需要先減去0x10000,得到0x02345,拆分成高10位00 0000 1000和低10位11 0100 0101。根據上面規則加上特定值後,高位代理值為D808,低位代理值為DF45,最終記憶體中的順序為D8 08 DF 45

JavaScript中的string與DOMString

在JavaScript中,所有的string型別(或者被稱為DOMString)都是使用UTF-16編碼的。

因此,當我們需要轉換成二進位制與後端進行通訊時,需要注意相關的編碼方式。

總結

本文通過對Unicode編碼和UTF-8和UTF-16兩種編碼方式進行介紹,讓大家瞭解Unicode編碼以及相關的兩種程式資料編碼方式。

本文是作為utfx.js原始碼分析的基礎知識儲備文章,在稍後的時間將會給大家帶來相關內容的後續文章——utfx.js原始碼解析,讓大家能夠了解在JavaScript中如何進行相關的編碼轉換。

相關文章