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

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

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

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

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

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

什麼是Unicode?

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

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

Unicode編碼定義了這個世界上幾乎所有字符(就是你眼睛看的字符比如ABC,漢字等)的數字表示,而且Unicode還兼容了很多老版本的編碼規範,例如你熟悉的 ASCII碼。

什麼是碼點?

我們國家的每一個人都對應唯一的一個身份證號,而Unicode也為了每個字符發了一張身份證,這張“身份證”上有一串唯一的數字ID確定了這個字符。

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

碼點是如何表示的呢?

先來說一聲碼點是如何表示的:

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

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

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

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

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

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

這個網址就是神器~

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

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

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

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

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

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

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

可以看到很詳細的 字符 https://xiaogd.net/。

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

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

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

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

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

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

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

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

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


碼點的取值範圍

碼點的取值範圍目前是 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

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

什麼是 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


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

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


什麼是增補平面?

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


代理區

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

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

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

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

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

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

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

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

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

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


在前面的鳥瞰圖中,我們看到了一片空白的區域,這就是所謂的代理區(Surrogate Area)了,代理區是 UTF-16 為了編碼增補平面中的字符而保留的,總共有 2048 個位置,均分為高代理區(D800–DBFF)和低代理區(DC00–DFFF)兩部分,各1024,這兩個區組成一個二維的表格,共有1024×1024=210×210=24×216=16×65536,所以它恰好可以表示增補的 16 個平面中的所有字符。

下面的圖片來自 wiki

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

什麼是代理對?

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

在圖中可以看到一些轉換的例子,如

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

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

那UTF-16為何要採用代理對?

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

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

最開始是採用定長二字節方案,但是無法滿足容量增長,因為兩個字節也就216 = 65536個而已,我們天朝的漢字就比這65536還多,那怎麼辦?擴唄~

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

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

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

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


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

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

這裡挖出 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的空間。依次來實現了擴容!

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

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


碼點到 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。


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

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

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

UTF-32

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

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

UTF-8


UTF-8的好處

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

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

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

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

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

你的方案的想法很美好,它試圖跟隨編號來自然增長,它還是可以編碼的,但在解碼時則遇到了困難。

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

可見,由於低位的碼位被“榨乾”了,導致單個位與多位間無法區分,所以你的方案是行不通的。

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

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

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

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

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

看個實例

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

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

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

喬哥:這還是非常粗糙的設計,如果我們想在這串字符中搜索“o”這個字符,它的編碼是 3,首先會找到3和53,這樣在匹配時也會匹配上 53 中的 3,這種設計會讓我們在實現匹配算法時不好實現啊。,

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

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

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

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

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

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

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

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

可以看到,由於固定位上的 0 和 1 的差別,使得二字節既不會與三字節的前兩字節相同,也不會它的後兩字節相同。

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

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

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

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


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

對於Unicode的編碼首先確定它的範圍,找到它是對應的https://baike.baidu.com/item/Unicode/750500?fr=aladdin。

對於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。

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

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

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

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

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

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


分享到:


相關文章: