轉載自:https://blog.csdn.net/zhusongziye/article/details/84261211
Unicode編碼!?
想必做過爬蟲的同學肯定被編碼問題困擾過,有 UTF-8、GBK、Unicode 等等編碼方式,但你真的了解其中的原理嗎?下面我們就來了解一下 Unicode 和 UTF-8 編碼到底有什么關系。
要弄清 Unicode 與 UTF-8 的關系,我們還得從他們的來源說起,下來我們從剛開始的編碼說起,直到 Unicode 的出現,我們就會感覺到他們之間的關系
ASCII碼
我們都知道,在計算機的世界里,信息的表示方式只有 0 和 1,但是我們人類信息表示的方式卻與之大不相同,很多時候是用語言文字、圖像、聲音等傳遞信息的。
那么我們怎樣將其轉化為二進制存儲到計算機中,這個過程我們稱之為編碼。更廣義地講就是把信息從一種形式轉化為另一種形式的過程。
我們知道一個二進制有兩種狀態:”0” 狀態 和 “1”狀態,那么它就可以代表兩種不同的東西,我們想賦予它什么含義,就賦予什么含義,比如說我規定,“0” 代表 “吃過了”, “1”代表 “還沒吃”。
這樣,我們就相當於把現實生活中的信息編碼成二進制數字了,並且這個例子中是一位二進制數字,那么 2 位二進制數可以代表多少種情況能?對,是四種,2^2,分別是 00、01、10、11,那 7 種呢?答案是 2^7=128。
我們知道,在計算機中每八個二進制位組成了一個字節(Byte),計算機存儲的最小單位就是字節,字節如下圖所示 :

所以早期人們用 8 位二進制來編碼英文字母(最前面的一位是 0),也就是說,將英文字母和一些常用的字符和這 128 中二進制 0、1 串一一對應起來,比如說 大寫字母“A”所對應的二進制位“01000001”,轉換為十六進制為 41。
在美國,這 128 是夠了,但是其他國家不答應啊,他們的字符和英文是有出入的,比如在法語中在字母上有注音符號,如 é ,這個怎么表示成二進制?
所以各個國家就決定把字節中最前面未使用的那一個位拿來使用,原來的 128 種狀態就變成了 256 種狀態,比如 é 就被編碼成 130(二進制的 10000010)。
為了保持與 ASCII 碼的兼容性,一般最高為為 0 時和原來的 ASCII 碼相同,最高位為 1 的時候,各個國家自己給后面的位 (1xxx xxxx) 賦予他們國家的字符意義。
但是這樣一來又有問題出現了,不同國家對新增的 128 個數字賦予了不同的含義,比如說 130 在法語中代表了 é,但是在希伯來語中卻代表了字母 Gimel(這不是希伯來字母,只是讀音翻譯成英文的形式)具體的希伯來字母 Gimel 看下圖

所以這就成了不同國家有不同國家的編碼方式,所以如果給你一串二進制數,你想要解碼,就必須知道它的編碼方式,不然就會出現我們有時候看到的亂碼 。
Unicode的出現
Unicode 為世界上所有字符都分配了一個唯一的數字編號,這個編號范圍從 0x000000 到 0x10FFFF (十六進制),有 110 多萬,每個字符都有一個唯一的 Unicode 編號,這個編號一般寫成 16 進制,在前面加上 U+。例如:“馬”的 Unicode 是U+9A6C。
Unicode 就相當於一張表,建立了字符與編號之間的聯系

它是一種規定,Unicode 本身只規定了每個字符的數字編號是多少,並沒有規定這個編號如何存儲。
有的人會說了,那我可以直接把 Unicode 編號直接轉換成二進制進行存儲,是的,你可以,但是這個就需要人為的規定了,而 Unicode 並沒有說這樣弄,因為除了你這種直接轉換成二進制的方案外,還有其他方案,接下來我們會逐一看到。
編號怎么對應到二進制表示呢?有多種方案:主要有 UTF-8,UTF-16,UTF-32。
1、UTF-32
先來看簡單的 UTF-32
這個就是字符所對應編號的整數二進制形式,四個字節。這個就是直接轉換。 比如馬的 Unicode 為:U+9A6C,那么直接轉化為二進制,它的表示就為:1001 1010 0110 1100。
這里需要說明的是,轉換成二進制后計算機存儲的問題,我們知道,計算機在存儲器中排列字節有兩種方式:大端法和小端法,大端法就是將高位字節放到底地址處,比如 0x1234, 計算機用兩個字節存儲,一個是高位字節 0x12,一個是低位字節 0x34,它的存儲方式為下:

UTF-32 用四個字節表示,處理單元為四個字節(一次拿到四個字節進行處理),如果不分大小端的話,那么就會出現解讀錯誤,比如我們一次要處理四個字節 12 34 56 78,這四個字節是表示 0x12 34 56 78 還是表示 0x78 56 34 12?不同的解釋最終表示的值不一樣。
我們可以根據他們高低字節的存儲位置來判斷他們所代表的含義,所以在編碼方式中有 UTF-32BE 和 UTF-32LE,分別對應大端和小端,來正確地解釋多個字節(這里是四個字節)的含義。
2、UTF-16
UTF-16 使用變長字節表示
① 對於編號在 U+0000 到 U+FFFF 的字符(常用字符集),直接用兩個字節表示。
② 編號在 U+10000 到 U+10FFFF 之間的字符,需要用四個字節表示。
同樣,UTF-16 也有字節的順序問題(大小端),所以就有 UTF-16BE 表示大端,UTF-16LE 表示小端。
3、UTF-8
UTF-8 就是使用變長字節表示,顧名思義,就是使用的字節數可變,這個變化是根據 Unicode 編號的大小有關,編號小的使用的字節就少,編號大的使用的字節就多。使用的字節個數從 1 到 4 個不等。
UTF-8 的編碼規則是:
① 對於單字節的符號,字節的第一位設為 0,后面的7位為這個符號的 Unicode 碼,因此對於英文字母,UTF-8 編碼和 ASCII 碼是相同的。
② 對於n字節的符號(n>1),第一個字節的前 n 位都設為 1,第 n+1 位設為 0,后面字節的前兩位一律設為 10,剩下的沒有提及的二進制位,全部為這個符號的 Unicode 碼 。
舉個例子:比如說一個字符的 Unicode 編碼是 130,顯然按照 UTF-8 的規則一個字節是表示不了它(因為如果是一個字節的話前面的一位必須是 0),所以需要兩個字節(n = 2)。
根據規則,第一個字節的前 2 位都設為 1,第 3(2+1) 位設為 0,則第一個字節為:110X XXXX,后面字節的前兩位一律設為 10,后面只剩下一個字節,所以后面的字節為:10XX XXXX。
所以它的格式為 110XXXXX 10XXXXXX 。
下面我們來具體看看具體的 Unicode 編號范圍與對應的 UTF-8 二進制格式

那么對於一個具體的 Unicode 編號,具體怎么進行 UTF-8 的編碼呢?
首先找到該 Unicode 編號所在的編號范圍,進而可以找到與之對應的二進制格式,然后將該 Unicode 編號轉化為二進制數(去掉高位的 0),最后將該二進制數從右向左依次填入二進制格式的 X 中,如果還有 X 未填,則設為 0 。
比如:“馬”的 Unicode 編號是:0x9A6C,整數編號是 39532,對應第三個范圍(2048 - 65535),其格式為:1110XXXX 10XXXXXX 10XXXXXX,39532 對應的二進制是 1001 1010 0110 1100,將二進制填入進入就為:
11101001 10101001 10101100 。


由於 UTF-8 的處理單元為一個字節(也就是一次處理一個字節),所以處理器在處理的時候就不需要考慮這一個字節的存儲是在高位還是在低位,直接拿到這個字節進行處理就行了,因為大小端是針對大於一個字節的數的存儲問題而言的。
綜上所述,UTF-8、UTF-16、UTF-32 都是 Unicode 的一種實現。
