[十]基礎數據類型之Unicode編碼簡介

編碼含義

關於編碼的含義,以前也說過,計算機只能存儲二進制序列
因此對於字符,保存的時候,須要進行編碼爲二進制,進行存儲
呈現的時候,須要將二進制進行解碼,轉換成字符的形式
 
有不少種編碼方式,好比ASCII
(American Standard Code for Information Interchange,美國信息交換標準代碼)
使用一個字節進行編碼,一個字節能夠表示的最大值爲255
很顯然,對於英語和其餘一些西歐語言來講,足夠了,英文字母總共才幾個對吧
 
那麼對於漢字呢?ASCII顯然是不夠用的,因此出現了GBK等以支持漢字
那麼,日語 韓語呢?他們固然也搞出來一些支持他們文字的編碼

Unicode誕生

因而,人們意識到他們應該提出一種標準方案來展現世界上全部語言中的全部字符,出於這個目的,Unicode誕生了
Unicode 是一種字符集,規定了符號對應的二進制代碼
至於這個二進制代碼如何存儲則沒有任何規定,也就是說它是一種編碼規定
是編碼字符集,而不是實際的編碼方案
 
最初Unicode使用一個16位長度的二進制序列,也就是最多支持   (2的16次方-1) 65536個字符,也就是0  ~ 65535
範圍是 0x0000 ~0xFFFF 
Unicode使用U+前綴, 加上編碼的值,來表示Unicode中的字符編碼
也就是  U+0000   ~  U+FFFF 

Unicode不夠用了

顯然,隨着更多字符的增長,  65536 是不夠用的
因而Unicode 不得不進行擴展,因而使用8位用做擴展位,形式以下
image_5bbedd5d_2d72
一個字節8位能夠表示 2的8次方-1 = 256 個數,最大能夠擴展爲 256 *65526=16777216 個
不過也不須要那麼多的字符, 僅僅使用了前面的17個
也就是
00 01 02 03 04 05 06 07 08 0A 0B 0C 0D 0E 0F 10
 
image_5bbedd5d_5f71
因而按照擴展位,劃分了17個維度,這每一個維度,叫作一個平面
17個平面,編號從 0~16
每一個平面 65536個字符
17個平面,擴展後總共能夠表示1114112個字符
擴展後的範圍爲
U+000000   ~  U+10FFFF  
 
其中對於第一個U+00  也就是U+0000到U+FFFF ,包含了最多見的字符 
被稱做 基本多語言平面  Basic Multilingual Plane, BMP
其他的U+010000   ~  U+10FFFF   被稱做輔助平面
至此你應該瞭解到了,什麼是Unicode的
他就是一個具備17個平面,每一個平面能夠容納65536個字符的一張巨大的字符碼錶
一個字符對應一個二進制序列
基本平面中使用四位十六進制數 在零號平面之外的字符則須要使用五位或六位十六進制數了

編碼方式

Unicode 沒有規定字符對應的二進制碼在計算機中如何存儲,只是規定了他的值是多少而已
一個字符對應的實際的值,咱們稱之爲代碼點 code point
那麼一個碼點實際的值怎麼存儲呢?
可能須要1個,可能須要2個,甚至可能須要3個或者4個字節來表示
對於計算機來講,面對着一堆字節,他們知道到底哪一個或者哪幾個是一個字符呢?
聽起來可能有點迷惑,不是知道具體的值了麼?怎麼還不知道如何表示?

好比數字1 他的碼點是1
假如我用兩個字節來存儲,每一個字節的前兩位我當作其餘的標誌位, 設置爲11
那麼可能結果是這樣子的11000000 11000001
顯然,他的值並非1
編碼方式只是能夠保證,你的字符是按照指定的字符集進行編碼的
也就是說若是你告訴我拿出來碼點爲1 的,我會把1100 0000 1100 0001 解析成數字1
可是並不能保證我保存的數據就是他的碼點的真值,0000 0001 ,中間形式是編碼方式說了算的

最直觀的例子就是網絡中報文的傳輸,都會附加本身的頭信息
因此中間傳輸的數據並非跟你發送的數據如出一轍,中間的數據就是編碼形式的存儲
可是,接收端接受解析後,就是跟你發送的數據同樣的,這就好像是你的字符
 
 
存儲的問題就是編碼方式的問題,就是表示成什麼形式的問題
編碼方式有UTF-8 UTF-16  UTF-32
UTF-8 是變長
UTF-32 是定長
UTF-16介於他們之間 2個字節或者4個字節
 

utf-16

UTF-16編碼以16位無符號整數爲單位
咱們把Unicode編碼記做U  編碼規則以下
若是U<0x010000, 也就是0x000000 ~ 0x00FFFF
U的UTF-16編碼,  就是U對應的16位無符號整數
若是U≥0x010000    也就是0x010000 ~ 0x10FFFF
咱們先計算下 U'=U-0x010000 
能夠得出來 U'  範圍是 0x000000 ~ 0x0FFFFF
顯然,  U'的最大值爲0xFFFFF   也就是最多20個1    也就是能夠被寫成20個二進制位
既然是20個二進制位,那麼咱們是否是能夠把它拆分紅兩組呢?
每組10個二進制位    00  0000  0000
它能表示的範圍是2的10次方=1024個 

BMP是2個字節,16位, 很顯然,若是把U' 拆分紅兩組,每組10個二進制位的話
每個都可以保存到2個字節內

因此Unicode標準規定:基本多語言平面內,U+D800..U+DFFF的值不對應於任何字符,爲代理區 ,其中又分爲高代理區和低代理區

U+D800 加上10個二進制位的數值的最大值,能夠獲得高代理區的範圍
U+D800 --->1101 10 00  0000  0000  + 0000 00 11  1111 1111 = 1101 1011 1111 1111 = 0xDBFF

下一個就是0xDBFF +1 = 0xDC00,因此低代理區從0xDC00  開始
0xDC00 加上10個二進制位的數值的最大值,能夠獲得低代理區的範圍
0xDC00----> 1101  1100  0000  0000 + 0000 00 11  1111 1111 = 1101111111111111 = 0xDFFF
高代理區範圍  U+D800 ~0xDBFF
低代理區範圍   0xDC00 ~ 0xDFFF

代理區間是U+D800....U+DFFF
因此UTF-16的編碼方式就是
先計算 U'=U-0x010000
而後將U'寫成二進制形式:yyyy yyyy yyxx xxxx xxxx
而後分別計算高位代理和低位代理
U+D800 --->1101 10 00  0000  0000 +   0000 00 yy  yyyy yyyy = 1101 10 yy  yyyy  yyyy

0xDC00----> 1101  1100  0000  0000 + 0000 00 xx  xxxx  xxxx =  1101  11xx  xxxx  xxxx

再精簡下步驟
1. 先計算 U'=U-0x010000
2. 而後將U'寫成二進制形式:yyyy yyyy yyxx xxxx xxxx
3.兩個值爲   1101 10 yy  yyyy  yyyy  /    1101  11xx  xxxx  xxxx
 
在這種處理方式下
若是一旦讀取到值爲U+D800 ~0xDBFF  ,他必然是代理區間中的一部分,也就非 0號平面中的字符
並且,咱們還可以根據值判斷出來,究竟是高位仍是低位,也就是第一個仍是第二個數值
只須要取出來這20位,而後再加上0x010000  這就是這個字符的碼點
 
能夠看得出來,對於基本平面中的字符,使用2個字節長度,16位表示,這被稱之爲一個代碼單元
對於除了基本平面外的輔助平面,使用4個字節長度來表示,也就是兩個代碼單元
 
以前咱們提到過,Unicode中的一個字符的值,被稱之爲一個碼點
顯然,一個碼點,可能被一個代碼單元存儲,也可能被兩個連續的代碼單元存儲

UTF-32

UTF-32編碼以32位無符號整數爲單位
Unicode的UTF-32編碼就是其對應的32位無符號整數
32位能夠表示的個數爲  2的32次方  爲4294967296,顯然綽綽有餘,沒什麼好說的了

UTF-8

UTF-8 是目前互聯網上使用最普遍的一種 Unicode 編碼方式,可變長
使用 1 - 4 個字節表示一個字符,根據字符的不一樣變換長度  
 
規則
能夠把編碼分解成兩部分,headbody
head中記錄須要字節的個數,使用第一個字節中1 的個數來表示
body記錄真實的數據, 若是須要不止一個字節,那麼body天然由多個字節組成,每一個body的前兩個字節爲10  其他爲數據
image_5bbedd5d_aec
UTF-8編碼的最大長度是4個字節,也就是最多有21個x 表示
Unicode的最大碼位0x10FFFF   0001 0000 1111 1111 1111 1111也只有21位
若是一個字節足夠表示
只須要一個字節便可表示,那麼第一位爲0  其他7位用於表示字符編碼的值
看得出來明顯的好處,能夠兼容ASCII 
若是一個字節不夠表示,根據範圍選擇,須要幾個字節,就有幾個1,而後補一個0   後面的body依次存放數據便可
想要肯定一個碼點的編碼
1. 查看範圍,根據上表肯定格式
2.轉換爲對應的二進制序列
3. 替換掉x便可

字節序

根據咱們上面描述的utf8 以及utf16都有可能使用不止一個字節進行編碼
其實還有不少其餘數據也不只僅是一個字節進行表達
在計算機中最終都是二進制序列的形式
好比utf-16中,雖然我能夠根據值肯定是否在0號平面內,仍是在擴展輔助平面的
可是,如何把一個二進制序列解析爲他的值,這是一個問題
好比
0000 0001  0000 0010 假如說這是一個十六進制數
他到底表示的是0102 仍是0201? 從哪邊開始解讀?
人會很天然的把左邊當高位,右邊當低位, 可是, 計算機, 你必須對他進行說明
這就是字節序的問題
其實就是高位和低位與內存地址高低的對應關係
分爲大端排序( Big endian  BE)和小端排序(Little endian  LE)
 
大端排序----高地址存儲低位  低地址存儲高位
小端排序----高地址存儲高位  低地址存儲低位
在內存中0x01020304的存儲方式
 
內存地址 4000 4001 4002 4003
BE           01     02     03     04
LE           04     03     02     01
 
其實只要記住,大小端說的都是低地址
大端就是低地址存儲高位
小端就是低地址存儲低位
相關文章
相關標籤/搜索