介紹 UTF-8 編碼
UTF-8 是一種針對 Unicode 的可變長度字符編碼。
針對 Unicode:UTF-8 是 Unicode 的實(shí)現(xiàn)方式之一。相當(dāng)于 Unicode 規(guī)定了字符對應(yīng)的代碼值,這個代碼值需要轉(zhuǎn)換為字節(jié)序列的形式,用于數(shù)據(jù)存儲、傳輸。代碼值到字節(jié)序列的轉(zhuǎn)換工作由 UTF-8 來完成。
可變長度字符編碼:UTF-8 使用一至四個字節(jié)對 Unicode 字符集中的所有有效代碼點(diǎn)進(jìn)行編碼。
- UTF-8 使用 1 個字節(jié)表示 ASCII 字符;
- UTF-8 使用 2 個字節(jié)表示帶有附加符號的拉丁文、希臘文等;
- UTF-8 使用 3 個字節(jié)表示其他基本多文種平面(BMP)中的字符(包含了大部分常用字,如大部分的漢字);
- UTF-8 使用 4 個字節(jié)表示 Unicode 輔助平面的字符。
技術(shù)是為了解決問題而生的,UTF-8 編碼是為了解決什么問題而設(shè)計(jì)的呢?UTF-8 是為了兼容 ASCII 編碼而設(shè)計(jì)的。
ASCII 編碼使用 1 個字節(jié)表示 ASCII 字符,而 Unicode 最初規(guī)定使用 2 個字節(jié)來表示所有的 Unicode 字符。如果使用 2 個字節(jié)來表示 ASCII 字符的話,那么含有大量 ASCII 字符的文本將浪費(fèi)大量的存儲空間。
UTF-8 編碼使用 1 個字節(jié)來表示 ASCII 字符,而且字面與 ASCII 碼的字面一一對應(yīng),這使得原來處理 ASCII 字符的軟件無須或只須做少部分修改,即可繼續(xù)使用。
UTF-8 編碼的規(guī)則
Unicode 和 UTF-8 之間的轉(zhuǎn)換關(guān)系表(x 字符表示碼點(diǎn)占據(jù)的位)
| 碼點(diǎn)的位數(shù) | 碼點(diǎn)起值 | 碼點(diǎn)終值 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | |
|---|---|---|---|---|---|---|---|---|---|
| 7 | U+0000 | U+007F | 1 | 0xxxxxxx | |||||
| 11 | U+0080 | U+07FF | 2 | 110xxxxx | 10xxxxxx | ||||
| 16 | U+0800 | U+FFFF | 3 | 1110xxxx | 10xxxxxx | 10xxxxxx | |||
| 21 | U+10000 | U+1FFFFF | 4 | 11110xxx | 10xxxxxx | 10xxxxxx | 10xxxxxx | ||
| 26 | U+200000 | U+3FFFFFF | 5 | 111110xx | 10xxxxxx | 10xxxxxx | 10xxxxxx | 10xxxxxx | |
| 31 | U+4000000 | U+7FFFFFFF | 6 | 1111110x | 10xxxxxx | 10xxxxxx | 10xxxxxx | 10xxxxxx | 10xxxxxx |

UTF-8 編碼的規(guī)則:
- 在 ASCII 碼范圍內(nèi)的代碼點(diǎn),UTF-8 使用 1 個字節(jié)表示。
- 大于 ASCII 碼范圍的代碼點(diǎn),UTF-8 使用多個字節(jié)表示。UTF-8 使用第一個字節(jié)的前幾位表示該 Unicode 字符的字節(jié)長度(第一個字節(jié)的開頭 1 的數(shù)目就是該 Unicode 字符的字節(jié)長度),其余字節(jié)的前兩位固定為 10,作為標(biāo)記
- 如果第一個字節(jié)的前<u>兩</u>位為 1,第三位為 0(110xxxxx),則表示 UTF-8 使用 2 個字節(jié)表示該 Unicode 字符;
- 如果第一個字節(jié)的前<u>三</u>位為 1,第四位為 0(1110xxxx),則表示 UTF-8 使用 3 個字節(jié)表示該 Unicode 字符;
- 依此類推;
- 如果第一個字節(jié)的前<u>六</u>位為 1,第七位為 0(1111110x),則表示 UTF-8 使用 6 個字節(jié)表示該 Unicode 字符;
UTF-8 編碼的字節(jié)含義:對于 UTF-8 編碼中的任意字節(jié) B:
- 如果 B 的第一位為 0(0xxxxxxx),則 B 獨(dú)立的表示一個 ASCII 字符;
- 如果 B 的第一位為 1,第二位為 0(10xxxxxx),則 B 為一個多字節(jié)表示的字符中的一個字節(jié);
- 如果 B 的前二 / 三 / 四 / 五 / 六位為 1,其余位為 0,則 B 為二 / 三 / 四 / 五 / 六個字節(jié)表示的字符中的第一個字節(jié)。
UTF-8 編碼示例
Unicode/UTF-8-character table (utf8-chartable.de)

通過 UTF-8 編碼表,我們可以看到中文字符 “一” 的 Unicode 代碼點(diǎn)為 "U+4E00",UTF-8 編碼結(jié)果為 "e4 b8 80",
對中文字符 “一” 進(jìn)行 UTF-8 編碼,是如何得到 "e4 b8 80" 的呢?我們下面來看。
"4E00" 的二進(jìn)制表示為 "0100 1110 0000 0000"。
UTF-8 使用 3 個字節(jié)表示常用的漢字,因此中文字符對應(yīng)的字節(jié)序列格式為:"1110xxxx 10xxxxxx 10xxxxxx"
于是中文字符 “一” 的 UTF-8 編碼結(jié)果為 "1110<u>0100</u> 10<u>111000</u> 10<u>000000</u>",它的十六進(jìn)制表示為 "e4 b8 80"
public static void main(String[] args) throws UnsupportedEncodingException {
byte[] bytes = "一".getBytes("UTF-8");
// [-28, -72, -128]
System.out.println(Arrays.toString(bytes));
}
UTF-8 編碼的優(yōu)劣局限
UTF-8 編碼的優(yōu)點(diǎn)
UTF-8 和 ASCII 兼容:ASCII 是 UTF-8 的一個子集。因?yàn)橐粋€純 ASCII 字符串也是一個合法的 UTF-8 字符串,所以現(xiàn)存的 ASCII 文本不需要轉(zhuǎn)換。為傳統(tǒng)的擴(kuò)展 ASCII 字符集設(shè)計(jì)的軟件通??梢圆唤?jīng)修改或很少修改就能與 UTF-8 一起使用。
任何面向字節(jié)的字符串搜索算法都可以用于 UTF-8 的數(shù)據(jù)(只要輸入僅由完整的 UTF-8 字符組成)。UTF-8 可以保證一個字符的字節(jié)序列不會包含在另一個字符的字節(jié)序列中。而有些比較舊的可變長度字符編碼(如Shift JIS)沒有這個特質(zhì),故它們的字符串搜索算法變得相當(dāng)復(fù)雜。
UTF-8 字符串可以由一個簡單的算法可靠地識別出來。由于 UTF-8 字節(jié)序列的設(shè)計(jì),如果一個疑似為字符串的序列被驗(yàn)證為 UTF-8 編碼,那么我們可以有把握地說它是 UTF-8 字符串。一個字符串在任何其它編碼中表現(xiàn)為合法的 UTF-8 的可能性很低,可能性隨著字符串長度的增長而減小。 舉例說明,字符值 C0、C1、F5 至 FF 從來沒有出現(xiàn)。為了更好的可靠性,可以使用正則表達(dá)式來統(tǒng)計(jì)非法過長和替代值(可以查看W3 FAQ: Multilingual Forms上的驗(yàn)證 UTF-8 字符串的正則表達(dá)式)。
UTF-8 編碼可以通過屏蔽位 和 移位操作快速讀寫:屏蔽位是指將字節(jié)的高位置零,以便獲取低位的值;移位操作是指將字節(jié)的低位移動到高位,以便獲取高位的值。這樣,可以快速讀取和寫入 UTF-8 編碼的字符。
UTF-8 編碼的缺點(diǎn)
UTF-8 編碼不利于使用正則表達(dá)式進(jìn)行讀音檢索
正則表達(dá)式可以進(jìn)行很多高級的英文模糊檢索。比如,[a-h] 表示 a 到 h 間的所有字母。
同樣 GBK 編碼的中文也可以這樣利用正則表達(dá)式,比如在只知道一個字的讀音而不知道怎么寫的情況下,也可用正則表達(dá)式檢索,因?yàn)?GBK 編碼是按讀音排序的。雖然正則表達(dá)式檢索并未考慮中文的多音字,但是由于中文的多音字?jǐn)?shù)量不多,不少多音字還是同音不同調(diào)類型的多音字,所以大多數(shù)情況下正則表達(dá)式檢索是還可以接受的。
但是 Unicode 漢字不是按讀音排序的,它是按部首排序,所以不利于用正則表達(dá)式進(jìn)行讀音檢索。在只知道一個字的部首而不知道如何發(fā)音的情況下,UTF-8 可用正則表達(dá)式檢索而 GBK 不行。
UTF-8 的 ASCII 字符只占用一個字節(jié),比較節(jié)省空間,但是更多字符的 UTF-8 編碼占用的空間就要多出1/2,特別是中文、日文和韓文(CJK)這樣的方塊文字,它們大多需要三個字節(jié)。
無法根據(jù) Unicode 字符數(shù)判斷出 UTF-8 文本占用的字節(jié)數(shù)。因?yàn)?UTF-8 是一種可變長度字符編碼。