阿里面試官讓我講講Unicode,我講了3秒說沒了,面試官說你可真菜

本文首發於微信公衆號:程序員喬戈裏html

喬哥:首先說說什麼是Unicode、碼點吧~要想搞懂,這些概念必須清楚java

什麼是Unicode?

下圖來自http://www.unicode.org/standard/WhatIsUnicode.html中的截圖程序員

Unicode編碼定義了這個世界上幾乎全部字符(就是你眼睛看的字符好比ABC,漢字等)的數字表示,並且Unicode還兼容了不少老版本的編碼規範,例如你熟悉的 ASCII碼。面試

什麼是碼點?

咱們國家的每個人都對應惟一的一個身份證號,而Unicode也爲了每一個字符發了一張身份證,這張「身份證」上有一串惟一的數字ID肯定了這個字符。算法

這串數字在整個計算機的世界具備惟一性,Unicode給這串數字ID起了個名字叫[碼點]。編程

碼點是如何表示的呢?

先來講一聲碼點是如何表示的:微信

U+XXXXXX 是碼點的表示形式,X 表明一個十六制數字,能夠有 4-6 位,不足 4 位前補 0 補足 4 位,超過則按是幾位就是幾位。編碼

字符A的ASCII碼是衆所周知是65吧,將65轉換成16進制就是41(16×4+(16^0)×1 = 65),按照規則前面補0,那麼字符A的碼點表示就是U+0041,依次類推B的碼點表示就是U+0042...等等,漢字"你"的字符表示是「U+4F60」....net

這個網址就是神器~翻譯

http://www.fileformat.info/info/unicode/char/search.htm?q=%E4%BD%A0&preview=entity

在輸入框1中進行搜索,在出來的結果2中就是這個字符的unicode碼點表示,不只如此,結果2還能夠繼續進行點擊查看更多詳情!

我點一下結果2給你看看:

對於網址: http://www.fileformat.info/info/unicode/char/4f60/index.htm

能夠看到很詳細的 字符 詳情

喬哥:好比我把這個網址中的unicode碼點替換爲dc00,看看它會出現什麼

http://www.fileformat.info/info/unicode/char/dc00/index.htm

你看這個它就沒有任何的碼點表示,而是提示這個「Non Private Use High Surrogate, First」,Surrogate翻譯過來是代理的意思,這個碼點對應的是代理區了,這個就涉及到unicode的三種編碼方式了(換句話就是碼點如何轉換爲utf-8或者utf-16或者utf-32),utf-16中用到了代理區這個概念。

碼點的取值範圍

碼點的取值範圍目前是 U+0000 ~ U+10FFFF,理論大小爲 10FFFF+1=110000(爲啥+1,由於從0開始嘛~)。

16機制嘛~後一個 1表明是 65536(16的4次方),由於是 16 進制,因此前一個 1 是後一個 1 的 16 倍,因此總共有1×16+1=17 個的 65536 的大小,粗略估算爲 17×6萬=102 萬,因此這是一個百萬級別的數。

爲了更好分類管理如此龐大的碼點數,把每 65536 個碼點做爲一個平面,總共 17 個平面。

而咱們說的代理區就在平面裏面,而平面又有不少講究。爲了幫你搞懂代理區,先來聊一聊這平面的事

平面,BMP,SP

什麼是平面?

由前面可知,碼點的所有範圍能夠均分紅 17 個 65536 大小的部分,這裏面的每個部分就是一個平面(Plane)。編號從 0 開始,第一個平面稱爲 Plane 0。

下圖來自http://rishida.net/docs/unicode-tutorial/part2

什麼是 BMP?

第一個平面便是 BMP(Basic Multilingual Plane 基本多語言平面),也叫 Plane 0,它的碼點範圍是 U+0000 ~ U+FFFF。這也是咱們最經常使用的平面,平常用到的字符絕大多數都落在這個平面內。

上圖中第一個花花綠綠的平面就是 BMP。

UTF-16 只須要用兩字節編碼此平面內的字符。

最經常使用的 BMP,它的碼點空間也有 6 萬多,若是把這些字符都放到一張圖片上,會是什麼狀況呢?GNU Unifont 就製做了一張這樣的圖片。見http://unifoundry.com/pub/unifont-7.0.03/unifont-7.0.03.bmp

下圖是它的一個縮略版本:

什麼是增補平面?

後續的 16 個平面稱爲 SP(Supplementary Planes)。顯然,這些碼點已是超過 U+FFFF 的了,因此已經超過了 16 位空間的理論上限,對於這些平面內的字符,UTF-16 採用了四字節編碼。

代理區

你可能還注意到前面的 BMP 縮略圖中有一片空白,這白花花一片亮瞎了咱們的猿眼的是啥呢?這就是所謂的**代理區(Surrogate Area)**了。

能夠看到這段空白從 D8~DF。其中前面的紅色部分 D800–DBFF 屬於高代理區(High Surrogate Area),後面的藍色部分 DC00–DFFF 屬於低代理區(Low Surrogate Area),各自的大小均爲 4×256=1024。

小萌:unicode碼點替換爲dc00的字符詳情:「Non Private Use High Surrogate, First」,說明是高代理的意思,而 DC00 恰好就在 D800–DBFF這個高代理區裏面,嘿嘿~

UTF-16如何用代理區編碼?

UTF-16 是一種變長的 2 或 4 字節編碼模式。對於 BMP 內的字符使用 2 字節編碼,其它的則使用 4 字節組成所謂的代理對來編碼。

在前面的鳥瞰圖中,咱們看到了一片空白的區域,這就是所謂的代理區(Surrogate Area)了,代理區是 UTF-16 爲了編碼增補平面中的字符而保留的,總共有 2048 個位置,均分爲高代理區(D800–DBFF)和低代理區(DC00–DFFF)兩部分,各1024,這兩個區組成一個二維的表格,共有1024×1024=2<sup>10</sup>×2<sup>10</sup>=2<sup>4</sup>×2<sup>16</sup>=16×65536,因此它剛好能夠表示增補的 16 個平面中的全部字符。

下面的圖片來自 wiki

什麼是代理對?

一個高代理區(即上圖中的Lead(頭),行)的加一個低代理區(即上圖中的Trail(尾),列)的編碼組成一對便是一個代理對(Surrogate Pair),必須是這種先高後低的順序,若是出現兩個高,兩個低,或者先低後高,都是非法的。

在圖中能夠看到一些轉換的例子,如

(D8 00 DC 00)—>U+10000,左上角,第一個增補字符

(DB FF DF FF)—>U+10FFFF,右下角,最後一個增補字符

那UTF-16爲什麼要採用代理對?

最開始是採用定長二字節方案,可是沒法知足容量增加,由於兩個字節也就2<sup>16</sup> = 65536個而已,咱們天朝的漢字就比這65536還多,那怎麼辦?擴唄~

因而轉向定長四字節,可是轉到4個字節雖然解決了容量的問題,又會引起了效率危機,好比一個字符A用一個字節就夠存了,你非要用4個字節存,以前1G的·文件如今可能要4G去存,這不費錢嗎~

那這咋辦?因而各路大牛開天闢地,創建本身的編碼方案,力圖在效率和容量上取到一個平衡,其中一位大牛創建了UTF-16的編碼方案!

看下面這個圖,能夠看到編碼不是遞增的,70-89的編碼沒有與之對應的字符。

這裏挖出 70-89 間的碼位,造成橫豎 10×10 的編碼空間,使得能再擴展 100 個編碼空間。原來 2 位 100 個空間損失了 20,爲啥這麼說,由於70-89是20個,這部分不參與編碼,那不就是少了20個嗎

可是這20個編碼經過造成 代理對 的方式又新增了100個代碼空間,一來一回多了 80。這樣一種變長方式也就是 UTF-16 所採用的。

小萌:哦,懂了~ 小萌:UTF-16至關於犧牲了高代理區(D800–DBFF)和低代理區(DC00–DFFF)兩部分空間,可是確新增了10241024=1665536的空間。依次來實現了擴容!

碼點到 UTF-16 如何轉換?

喬哥:繼續上個例子。轉換分紅兩部分:

1. BMP 中直接對應,無須作任何轉換,也就是若是U<0x10000,U的UTF-16編碼就是U對應的16位無符號整數;

2. 增補平面 SP 中,則須要作相應的計算。也就是若是U≥0x10000的狀況

咱們先計算U'=U-0x10000,而後將U'寫成二進制形式:yyyy yyyy yyxx xxxx xxxx,U的UTF-16編碼(二進制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx。

Unicode編碼0x20C30,減去0x10000後,獲得0x10C30,寫成二進制是:0001 0000 1100 0011 0000。用前10位依次替代模板中的y,用後10位依次替代模板中的x,就獲得:1101100001000011 1101110000110000,轉換爲16進制即0xD843 0xDC30。

注意:以上計算方式僅用於說明轉換原理,不表明實際採用的計算方式。

UTF-32

咱們說碼點最大的 10FFFF 也就 21 位,而 UTF-32 採用的定長四字節則是 32 位,因此它表示全部的碼點不但毫無壓力,反而綽綽有餘,因此只要把碼點的表示形式之前補 0 的形式補夠 32 位便可。這種表示的最大缺點是佔用空間太大。

再來看稍複雜一點的 UTF-8。

UTF-8

UTF-8的好處

小萌:按照數字遞增進行編碼,例以下圖中,雖然簡單,但起碼也是一種編碼,哈哈~。

編碼方案1 字符
0 h
1 e
2 l
3 a
4 v
5 z
6 y
7 i
... ...

你的方案的想法很美好,它試圖跟隨編號來天然增加,它仍是能夠編碼的,但在解碼時則遇到了困難。

可見,因爲低位的碼位被「榨乾」了,致使單個位與多位間沒法區分,因此你的方案是行不通的。

下圖中的編碼方案2是個人改進方案。

這是個人第二種編碼方案,既然以前的沒法區分,那我就把低位空間騰出來,5 及以上的就不使用了5,6,7...到49這些編碼都不使用了,直接跳到50。而後引入一條變長解碼規則:

從左向右掃描,讀到 5 如下數字按單個位解碼;讀到 5 或以上數字時,把當前數字及下一個數字兩位一塊兒讀上來解碼。

看個實例

0和1是5如下的(5 如下數字按單個位解碼),因此解碼出來he,而當讀取到了5(讀到 5 或以上數字時,把當前數字及下一個數字兩位一塊兒讀上來解碼。),那麼5和3鏈接起來就是53,查一下編碼表53就是 「你」,這種方案避免了歧義。

喬哥:這仍是很是粗糙的設計,若是咱們想在這串字符中搜索「o」這個字符,它的編碼是 3, 首先會找到3和53,這樣在匹配時也會匹配上 53 中的 3,這種設計會讓咱們在實現匹配算法時很差實現啊。,

其實關鍵就在於用高位保留位來作區分,缺點就是有效編碼空間少了

UTF-8 是變長的編碼方案,能夠有 1,2,3,4 四種字節組合。UTF-8 採用了高位保留方式來區別不一樣變長,以下:

能夠看到,因爲最高位不一樣,多字節中不會包含一字節的模式。對於 UTF-8 而言,二字節的模式也不會包含在三字節模式中,也不會在四字節中;三字節模式也不會在四字節模式中,這樣就解決上面所說的搜索匹配難題。

能夠看到,因爲固定位上的 0 和 1 的差異,使得二字節既不會與三字節的前兩字節相同,也不會它的後兩字節相同。

這也每當進行搜索的時候,每一個二字節和三字節的編碼沒有重疊,由於最高位不一樣呀~因此不會出現搜索同一個出現兩個的結果。不過就是有效編碼空間少了。

UTF-8如何與碼點進行轉換

Unicode編碼(十六進制) UTF-8 字節流(二進制)
000000-00007F 0xxxxxxx
000080-0007FF 110xxxxx 10xxxxxx
000800-00FFFF 1110xxxx 10xxxxxx 10xxxxxx
010000-10FFFF 11110xxx10xxxxxx10xxxxxx10xxxxxx

對於Unicode的編碼首先肯定它的範圍,找到它是對應的幾字節

對於0x00-0x7F之間的字符,UTF-8編碼與[ASCII編碼]徹底相同。

「漢」字的Unicode編碼是0x6C49。0x6C49在0x0800-0xFFFF之間,使用3字節模板:1110xxxx 10xxxxxx 10xxxxxx。將0x6C49寫成二進制是:0110 1100 0100 1001, 用這個比特流依次代替模板中的x,獲得:11100110 10110001 10001001,即E6 B1 89。

本文首發於微信公衆號:程序員喬戈裏

若是是頭條用戶,能夠在個人頭條號程序員喬戈裏後臺回覆 資源獲取價值59998元的編程和考研資料 以爲文章不錯的歡迎關注個人WX公衆號:程序員喬戈裏
我是BAT大廠後臺開發工程師,,專一分享技術乾貨/編程資源/求職面試/成長感悟等,關注送5000G編程資源和本身整理的一份幫助很多人拿下java的offer的面經附答案,免費下載CSDN資源。

本文由博客一文多發平臺 OpenWrite 發佈!

相關文章
相關標籤/搜索