記錄一下字符編碼的有關事項,這篇文章先說說字符編碼的一些歷史和原理丰介。
1.ASCII編碼與ANSI標準
1字節(jié)(1B) = 8比特(8b) = 8個二進制位
1B的信息量 0b00000000 ~ 0b11111111 = 2^8 = 256個狀態(tài)
標準ASCII編碼使用1B的后7位,而第一位默認置0,即:
0b00000000 ~ 0b01111111 = 2^7 = 128個狀態(tài)
這128個狀態(tài)位存儲了控制字符贝室、數(shù)字契讲、大小寫字母和其他符號。詳見ASCII編碼表[]
漸漸地滑频,128個狀態(tài)不再能滿足人們的需求捡偏,不同國家(語言區(qū))都想把自己語言的字符編進ASCII編碼中,于是人們紛紛自發(fā)的使用起了1B的另外128個狀態(tài)峡迷,稱為擴展ASCII編碼银伟,其使用1B的后7位,同時第一位默認置1绘搞,即:
0b10000000 ~ 0b11111111 = 2^7 = 128個狀態(tài)
由于是自發(fā)行為彤避,每個國家(語言區(qū))都對擴展ASCII編碼有自己獨特的定義,因此彼此之間的擴展ASCII編碼是不能通用的夯辖。(標準ACII編碼區(qū)仍然通用)
麻煩還不止于此琉预,部分國家(語言區(qū))的字符數(shù)量眾多(如漢字就是十萬級別的數(shù)量),顯然擴展ASCII編碼的128個狀態(tài)不能滿足蒿褂,于是有了下面的一種編碼方式(ANSI標準編碼):
- 保留標準ASCII編碼的128個字符不變圆米,稱為半角
- 其余的字符用兩個字節(jié)來表示,這樣理論上能新增256*256=65536個編碼啄栓,稱為全角
這也是大家最早所認知的下面這句話的由來
英文(半角)占一個字節(jié)娄帖,漢字(全角)占兩個字節(jié)
全角字符中用于編碼的兩個字節(jié)被稱為高位字節(jié)和低位字節(jié)
全角字符 = 高位字節(jié) + 低位字節(jié)
高位字節(jié)和低位字節(jié)都可以選擇用三種策略編碼:
- 標準ASCII編碼的128個狀態(tài)
- 擴展ASCII編碼的128個狀態(tài)
- 以上都用的256個狀態(tài)
不難發(fā)現(xiàn)當這兩個字節(jié)都使用標準ASCII編碼的128個狀態(tài)時,計算機是無法分辨這是兩個半角字符昙楚,還是一個全角字符的块茁。因此全角的編碼區(qū)域?qū)嶋H上只有:
256*256 - 128*128 = 49152個
針對這個5萬個左右的編碼區(qū),不同國家(語言區(qū))制定了不同的編碼標準桂肌,我們熟悉的有:
- GB2312数焊,大陸1980年標準(其中一級漢字3755個,二級漢字3008個崎场,包括拉丁字母佩耳、希臘字母、日文平假名及片假名字母谭跨、俄語西里爾字母在內(nèi)的全角字符682個干厚。)
- GBK,大陸1995年標準(GB2312的擴展螃宙,增加不常用漢字與字符蛮瞄,總編碼量擴展到23940)
- BIG-5,臺灣標準
2. Unicode編碼
隨著互聯(lián)網(wǎng)的發(fā)展谆扎,信息交流變得越來越頻繁挂捅,這就促使一種“大一統(tǒng)”的編碼方式出現(xiàn)。這種編碼可以將世界上所有的字符都編入其中堂湖,且每個狀態(tài)位和字符都唯一對應闲先。正如它的名字代表的意思那樣状土,Unicode編碼做到了。
Unicode將編碼和存儲這兩個邏輯過程獨立開來伺糠。
- 世界上的每一個字符都有且只有一個Unicode編碼方案即:字符S->Unicode(S)
- 每一個Unicode碼都可以通過的多種方案來存儲蒙谓,這些方案的名稱即是我們常聽到的UTF-8、UTF-16训桶、UTF-32等等累驮。
2.1 編碼邏輯
下面先說一下Unicode的編碼方式:
Unicode碼的編碼范圍:0x000000 ~ 0x10FFFF
它的后四位稱為一個plane:0x0000 ~ 0xFFFF = 2^16 = 65536個狀態(tài)
前兩位代表plane的編號,一共有:0x00 ~ 0x10 = 17個plane
所以Unicode編碼一共有:17 * 65536 = 1114112個狀態(tài)
這足以將世界上所有的字符都編碼進去舵揭,而且還有很大的富余谤专。同時別忘了,預留的空白plane可遠遠多于17個(16*16=256個)
事實上琉朽,在Unicode 5.0版本中只用到了0,1稚铣,2箱叁,14,15惕医,16這幾個編號plane中的238605個狀態(tài)
2.2 存儲邏輯
Unicode編碼總算是將全世界的字符都“裝”下了耕漱,當然為了達到此目的,經(jīng)過Unicode編碼后的字符所占空間變的很大抬伺。本來只占一個字節(jié)的普通ASCII字符和占兩個字節(jié)的ANSI字符統(tǒng)統(tǒng)都變成了占三個字節(jié)的Unicode字符螟够,造成了空間的極大浪費。因此Unicode編碼的編碼方式和存儲方式分開獨立實現(xiàn)峡钓,存儲傳輸方案專注于實現(xiàn)Unicode編碼如何節(jié)省存儲空間妓笙。
常見的存儲傳輸方案有:UTF-8,UTF-16能岩,UTF-32等
UTF-8方案
UTF-8最大的一個特點寞宫,就是它是一種變長的存儲方式。它可以使用1~4個字節(jié)存儲一個Unicode碼拉鹃,根據(jù)不同的Unicode碼而變化存儲字節(jié)的長度辈赋。
UTF-8的存儲規(guī)則很簡單,只有二條:
- 對于小于0x7F的Unicode碼膏燕,UTF-8編碼只有一個字節(jié)钥屈,字節(jié)的第一位設為0,后面7位為Unicode碼坝辫。這也是為了英語字母的UTF-8編碼和普通ASCII碼是相同的篷就。
- 對于其他Unicode碼,落入下表的相應的范圍中近忙。其中x代表空白位腻脏,用Unicode碼補充鸦泳。
Unicode符號范圍(十六進制) | UTF-8存儲方式(二進制) |
---|---|
000000 ~ 00007F | 0xxxxxxx |
000080 ~ 0007FF | 110xxxxx 10xxxxxx |
000800 ~ 00FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
010000 ~ 10FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
UTF-16方案
UTF-16方案也是一種變長存儲方式,它采用兩個字節(jié)或四個字節(jié)來存儲Unicode碼永品。
當Unicode碼位于編號為00的plane(即0x000000 ~ 0x00FFFF之間)時做鹰,Unicode碼正好對應了兩個字節(jié)(16位二進制)的長度。即使用兩個字節(jié)順序存儲鼎姐。
當Unicode碼位于其他編號的plane(即0x010000 ~ 0x10FFFF)時钾麸,Unicode碼減去0x10000后正好對應成20位二進制,依次填入以下四個字節(jié)的20個空位中:
110110xx xxxxxxxx 110111xx xxxxxxxx
前兩個字節(jié)稱為高位WORD炕桨,以110110開頭饭尝;后兩個字節(jié)稱為低位WORD,以110111開頭献宫。
UTF-32方案
UTF-32方案是一種定長存儲方案钥平,它總是采用4個字節(jié)存儲Unicode碼,由于Unicode碼只有24位姊途,于是UTF-32方案不對Unicode碼做任何變化直接存儲涉瘾。
有人會問,用3個字節(jié)不也是可以完全存儲Unicode的所有編碼嗎捷兰?我個人的理解是立叛,由于Unicode的前身:通用字符集(Universal Character Set, UCS)分為了2字節(jié)編碼的UCS-2和4字節(jié)編碼的UCS-4。為了使編碼方案統(tǒng)一贡茅,UCS-4承諾不再向0x10FFFF之后編碼秘蛇,并由UCS-2作為編號為0的plane(Basic Multilingual Plane, BMP)共同組成Unicode編碼。3者之間的關系為:
UCS-4編碼中0x00000000 ~ 0x0010FFFF的部分組成了Unicode編碼
Unicode編碼中0x000000 ~ 0x00FFFF的部分組成了UCS-2編碼
因此UTF-16作為UCS-2編碼的存儲方案顶考,UTF-32作為UCS-4編碼的存儲方案使用4個字節(jié)赁还,還是仍然保留著。
關于字節(jié)序(Byte Order Mark, BOM)
在UTF-16和UTF-32中存在著Little endian (LE)和Big endian (BE)兩種傳輸字節(jié)流的方式(顯然很蛋疼)
為了讓機器識別出這兩種傳輸方式驹沿,在Unicode編碼規(guī)范中設定了一個叫做"ZERO WIDTH NO-BREAK SPACE"的狀態(tài)位0x00FEFF秽浇,它不對應任何實際含義的字符,且總是出現(xiàn)在文件的開頭甚负,用于標志該文件是使用什么方式讀取字節(jié)流的(LE or BE)柬焕。按照上文三種存儲方案的規(guī)則:
編碼方案 | BOM(十六進制) |
---|---|
UTF-8 | EF BB BF |
UTF-16LE | FF FE |
UTF-16BE | FE FF |
UTF-32LE | FF FE 00 00 |
UTF-32BE | 00 00 FE FF |
可見由于UTF-8不存在LE或BE的區(qū)分,因此它的BOM只有一種梭域,可有可無斑举。所以在UTF-8的方案下,有UTF-8 BOM和UTF-8 無BOM兩種病涨,而它們的區(qū)別僅僅是文件開頭有沒有EF BB BF 這三個字節(jié)罷了