最近在看書的時候突然糾結(jié)于Unicode相關(guān)字符編碼,查了一些資料偎血,并寫了這篇文章,順帶做下筆記盯漂,希望能幫到一些人颇玷。文章如果有寫的不妥的或者不正確的地方還請大家糾正。
Unicode 編碼
Unicode是一個符號集就缆,它對世界上大部分的文字系統(tǒng)進行了整理帖渠、編碼,使得電腦可以用更為簡單的方式來呈現(xiàn)和處理文字竭宰。解決傳統(tǒng)的字符編碼方案的局限空郊。
歷史上存在兩個獨立的嘗試創(chuàng)立單一字符集的組織份招,即國際標(biāo)準(zhǔn)化組織(ISO)和非營利機構(gòu)統(tǒng)一碼聯(lián)盟。前者開發(fā)的 ISO/IEC 10646 項目狞甚,后者開發(fā)的統(tǒng)一碼項目脾还。因此最初制定了不同的標(biāo)準(zhǔn)。他們不久便發(fā)現(xiàn)對方的存在入愧,大家為著相同的目的而工作鄙漏,最后他們合并雙方的工作成果。統(tǒng)一碼(Unicode)的編碼方式與ISO 10646的通用字符集(Universal Character Set, 簡稱UCS)概念相對應(yīng)棺蛛。
統(tǒng)一碼的編碼方式使用16位編碼空間怔蚌,也就是每個字符占用2個字節(jié),最多可表示2^(16)個字符旁赊,基本滿足各種語言的需求桦踊,且實際上16為編碼空間并未完全使用,其中保留了大量空間作為未來備用终畅。這里所說的16位編碼空間即統(tǒng)一碼的0號平面(也稱“基本多文種平面”籍胯,Basic Multilingual Plane,簡稱BMP)离福,目前統(tǒng)一碼版本中另外定義了16個輔助平面杖狼,這樣就需求21位編碼空間,即 16+5 位妖爷,一共17個平面(不局限于)蝶涩,每個平面擁有2^(16)個代碼點。如下表所示(摘自Wikipedia):
ASCII
ASCII(“阿斯柯”) 是國際上普遍采用的一種字符編碼系統(tǒng)絮识,由8位二進制進行編碼绿聘,最高位恒為0,因此可以定義128個字符次舌,其中包括10個十進制數(shù)字熄攘、52個英文大小寫字母(A~Z, a~z)等。
UTF-8
UTF(Unicode Transformation Format, Unicode字符集轉(zhuǎn)換格式)彼念,UTF-7挪圾、UTF-8、UTF-16国拇、UTF-32洛史、GB18030...只是Unicode的一種實現(xiàn)方式惯殊,即怎樣將 Unicode 定義的數(shù)字轉(zhuǎn)換成程序數(shù)據(jù)酱吝。
UTF-8 編碼,以8位無符號整數(shù)為單位進行編碼土思,是針對Unicode的可變長字符編碼务热,UTF-8 是 ASCII 編碼的父集忆嗜,也就是說,UTF-8 與 ASCII 編碼兼容崎岂,如:對于0x000000-0x00007F之間的字符捆毫,即前128個字符,UTF-8 編碼與 ASCII 編碼完全相同冲甘。這使得原來處理 ASCII 碼字符的軟件無須或只須做少部分修改绩卤,即可繼續(xù)使用,UTF-8 編碼應(yīng)用廣泛江醇,基本所有互聯(lián)網(wǎng)協(xié)議都支持 UTF-8 編碼濒憋,是目前編碼方式中優(yōu)先采用的方式之一。
關(guān)于Unicode 與 UTF-8 編碼之間的轉(zhuǎn)換關(guān)系陶夜,如下表所示:
Unicode 標(biāo)量值(0x) | UTF-8(0b) | 備注 |
---|---|---|
000000-00007F | 0xxxxxxx | 與ASCII編碼字符相對應(yīng)凛驮,最高位恒為0。占用一個字節(jié) |
000080-0007FF | 110xxxxx-10xxxxxx | 第一個字節(jié)由110開始条辟,接著的字節(jié)由10開始黔夭。占用兩個字節(jié) |
000800-00D7FF | 1110xxxx-10xxxxxx-10xxxxxx | 占用3個字節(jié) |
00D800-00DFFF | - | D800-DBFF 代表 UTF-16的高半?yún)^(qū)。DC00-DFFF 代表 UTF-16的低半?yún)^(qū) |
00E000-00FFFF | 1110xxxx-10xxxxxx-10xxxxxx | 占用3個字節(jié) |
010000-10FFFF | 11110xxx-10xxxxxx-10xxxxxx-10xxxxxx | 最高字節(jié)前1的數(shù)量代表字節(jié)數(shù)羽嫡,如:1110即3個字節(jié)本姥。占用4個字節(jié) |
在基本多文種平面中約定00D800-00DFFF這范圍用于UTF-16擴展標(biāo)識輔助平面(即低位兩個字節(jié)),在UTF-16 中會詳細(xì)介紹杭棵。
舉個例子扣草,漢字“聽”的 Unicode 編碼是U+542C,轉(zhuǎn)成UTF-8颜屠,步驟如下:
- 由上表可得出辰妙,“聽”字的 Unicode 編碼屬于U+0800到U+D7FF區(qū)域,說明該字占用3個字節(jié)甫窟,按照1110xxxx-10xxxxxx-10xxxxxx進行填充密浑。
- U+542C換算成二進制:0101-0100-0010-1100。
- 從低位向高位填充粗井,代替x尔破,11100101-10010000-10101100。
- 得出漢字“聽”的UTF-8編碼:0xE590AC浇衬。
從Unicode 2.0開始懒构,Unicode采用了與ISO 10646-1相同的字庫和字碼;ISO也承諾耘擂,ISO 10646將不會替超出U+10FFFF的UCS-4編碼賦值胆剧,以使得兩者保持一致。2003年11月 UTF-8 被 RFC 3629重新規(guī)范,只能使用原來Unicode定義的區(qū)域秩霍,U+0000到U+10FFFF篙悯。如果以上都能理解,那么下表就非常好理解了(摘自Wikipedia):
之前有較多的人在微博上私信我@tingxins铃绒,關(guān)于這表格鸽照,疑惑頗多,因此在此處進行補充并簡單解釋一下颠悬,希望能幫到讀者矮燎。C0,C1非常好理解赔癌,不再詳述漏峰。我們來看看F5-FF的頭字節(jié),為什么是非法的届榄?我們可以以 U+10FFFF 為例浅乔,轉(zhuǎn)UTF-8編碼后,可以得出頭字節(jié)二進制流為11110100铝条,即F4靖苇,基于 RFC 3629 規(guī)范,因此可得出大于F4頭字節(jié)的可以理解成非法的或者不可能出現(xiàn)的編碼班缰,就7或8字節(jié)序列的頭字節(jié)而言贤壁,更是違反了早期UTF-8編碼不可超過6字節(jié)序列的規(guī)范。(更新于 2017-2-18)
UTF-8 小結(jié)
- 在UTF-8文件的開首埠忘,以EF,BB,BF代表脾拆,以顯示這個文本文件是以UTF-8編碼。
- 字節(jié)0xFE和0xFF在UTF-8編碼中從未用到莹妒,同時名船,UTF-8以字節(jié)為編碼單元,它的字節(jié)順序在所有系統(tǒng)中都是一様?shù)闹嫉。瑳]有字節(jié)序的問題渠驼,也因此它實際上并不需要BOM(字節(jié)順序標(biāo)記,Byte-Order Mark)鉴腻,但在UTF-16中用來標(biāo)記存儲方式(大端小端)迷扇。
- ASCII和UTF-8兩種編碼方式下是一樣的,可以說UTF-8是ASCII編碼的父集爽哎。
現(xiàn)在我們已經(jīng)知道了UTF-8的含義蜓席,以及其編碼原理,下面我們來探究一下 UTF-16 編碼方式课锌。
UTF-16
UTF-16 編碼厨内,以16位無符號整數(shù)為單位進行編碼。上文中所提及到的“基本多文種平面”的編碼空間中保留了一塊區(qū)域(從U+D800到U+DFFF),該區(qū)域不映射Unicode字符隘庄,UTF-16就是利用保留下來的0xD800-0xDFFF編碼空間來對U+10000到U+10FFFF(即輔助平面)進行字符映射的。
在 UTF-16 編碼中癣亚,從U+0000至U+D7FF以及從U+E000至U+FFFF的編碼空間的映射關(guān)系同 Unicode丑掺,相對應(yīng)于ISO通用字符集中的USC-2。從U+10000到U+10FFFF的編碼空間述雾,UTF-16用一對16比特長的碼元(即32bit,4Bytes)進行編碼街州,熟稱代理對(Surrogate Pair).
0xD800-0xDFFF編碼空間分成兩部分(即上述所說的代理對):
- UTF-16的高位代理:從U+D800至U+DBFF,也稱前導(dǎo)代理(lead surrogates)玻孟。
- UTF-16的低位代理:從U+DC00至U+DFFF唆缴,也稱后尾代理(trail surrogates)。
UTF-16 輔助平面編碼方式比較巧妙黍翎,從U+10000到U+10FFFF面徽,共計FFFFF個,即2(20)個匣掸,至少需要20位來表示趟紊,我們再來看代理對,先看高半?yún)^(qū)碰酝,從U+D800到U+DBFF霎匈,共計3FF個,即2(10)個送爸,同理低半?yún)^(qū)也是2(10)個铛嘱,正好為2(20)個代理對,這也是“基本多語言平面”中保留不對應(yīng)于Unicode字符的2048個碼位的原因袭厂。下面我們來看一張表:
lead S \ trail S | DC00 | DC01 | DC02 | ... | DFFF |
---|---|---|---|---|---|
D800 | 10000 | 10001 | 10002 | ... | 103FF |
D801 | 10400 | 10401 | 10402 | ... | 107FF |
D802 | 10800 | 10801 | 10802 | ... | 10BFF |
? | ? | ? | ? | ? | ? |
DBFF | 10FC00 | 10FC01 | 10FC02 | ... | 10FFFF |
舉個例子墨吓,古意大利字母"??"的Unicode編碼為U+10300,轉(zhuǎn)成UTF-16纹磺,步驟如下:
- 在0x10300的基礎(chǔ)上先減去0x10000 --> 0x00300肛真,轉(zhuǎn)成二進制:0000-0000-0011-0000-0000。
- 得出高10位(0000-0000-00)和低10位(11-0000-0000)
- 添加0xD800到高10位(不足補0)爽航,得出UTF-16高位:0xD800 + 0x0000 --> 0xD800
- 添加0xDC00到低10位(不足補0)蚓让,得出UTF-16低位:0xDC00 + 0x0300 --> 0xDF00
- 得出古意大利字母"??"的UTF-16BE編碼:U+D800DF00
關(guān)于Unicode 與 UTF-16 編碼之間的轉(zhuǎn)換關(guān)系,如下表所示:
Unicode 標(biāo)量值(0x) | UTF-16(0b) | 備注 |
---|---|---|
U+0000-U+FFFF | xxxxxxxx xxxxxxxx | 占用兩個字節(jié)讥珍,同 Unicode |
U+10000-U+10FFFF | 110110xxxxxxxxxx 110111xxxxxxxxxx | 占用四個字節(jié) |
由上表可看出历极,UTF-16無法兼容ASCII編碼。
UTF-16 存儲形式
想必讀者現(xiàn)在有這樣一個疑惑衷佃,UTF-16 是以16位無符號整數(shù)位單位進行編碼趟卸,即每個字符占用兩個字節(jié),如:在Mac和Window上,對字節(jié)順序的理解是不一樣的锄列,這時就出現(xiàn)了一個問題图云,同一字節(jié)流可能會被解釋為不同內(nèi)容,以字符“心“為例邻邮,該字符十六進制編碼為U+5FC3竣况,按兩個字節(jié)進行拆分:5F和C3,在Mac上讀取時是從低字節(jié)開始筒严,那么在Mac OS會認(rèn)為此U+5FC3編碼為U+C35F丹泉,顯示字符為"?",而在Windows上從高字節(jié)開始讀取鸭蛙,則編碼為U+5FC3的字符為“心”摹恨。為了解決該問題,字節(jié)順序標(biāo)記(Byte-Order Mark, BOM)誕生娶视,字符U+FEFF如果出現(xiàn)在字節(jié)流的開頭晒哄,則用來標(biāo)識該字節(jié)流的字節(jié)序,是高位在前還是低位在前肪获,反之同理揩晴。這兩種字節(jié)序在計算機我們通常稱大端和小端,下面我們來繼續(xù)探究一下贪磺。
大端存儲和小端存儲
大端存儲(Big Endian, 簡稱BE):一個字中的高位字節(jié)放在內(nèi)存中這個字區(qū)域的低地址硫兰。小端存儲(Little Endian, 簡稱LE):即一個字中的低位字節(jié)放在內(nèi)存中這個字區(qū)域的低地址處。
還是以古意大利字母"??"為例寒锚,我們剛已計算出其UTF-16編碼為U+D800DF00劫映,如果采用大端存儲,編碼存儲的序列為D800 DF00刹前,采用小端存儲泳赋,則為00D8 00DF。這個兩個存儲模式的區(qū)別在于字中字節(jié)的存儲順序不同喇喉,而字的存儲順序是相同的祖今。再看幾個例子(摘自Wikipedia):
UTF-16 小結(jié)
- 在UTF-16文件的開首,以FEFF 或者 FFFE代表拣技,以顯示這個文本文件是以BE存儲編碼還是以LE存儲編碼千诬。
- UTF-16編碼可以說是UCS-2的父集,對于小于0x10000的Unicode碼膏斤,UTF-16編碼就等于UCS碼徐绑,也可以說UTF-16編碼就等于Unicode標(biāo)量值。
- UTF-16 VS UTF-8莫辨,個人覺得這兩種編碼方式?jīng)]有可比性傲茄,主要取決于字符本身主要集中在哪個平面毅访,兩者都是可變長度編碼。
- UTF-16 VS UCS-2[1]盘榨,如果這個字超過U+FFFF(如:U+10000至U+10FFFF)喻粹,那么就無法用UCS-2的格式編碼,UTF-16可看成是UCS-2的父集草巡。
相關(guān)文章及鏈接
- 字符編碼筆記:ASCII守呜,Unicode和UTF-8
- Unicode Wikipedia
- UTF-8 Wikipedia
- 字節(jié)順序標(biāo)記 Wikipedia
- UTF-16 Wikipedia
- Unicode 字符平面映射
-
UCS即ISO 10646的通用字符集(Universal Character Set, 簡稱UCS),UCS-2我們可以簡單理解為UTF-16捷犹,同樣使用16位的編碼空間弛饭。 ?