姓名:何瑞 ????????? 學(xué)號(hào):17021211237
原文鏈接:http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
原文作者:阮一峰
【嵌牛導(dǎo)讀】:字符編碼是計(jì)算機(jī)技術(shù)的基石摘悴,想要熟練使用計(jì)算機(jī)朱躺,就必須懂得一點(diǎn)字符編碼的知識(shí)绍昂,作為程序員的我們字符編碼的知識(shí)也就是基礎(chǔ)中的基礎(chǔ)了篮愉,本文作者用通俗易懂的語(yǔ)言記錄了自己對(duì)字符編碼的學(xué)習(xí)礼殊,很適合初學(xué)者學(xué)習(xí)參考流礁。
【嵌牛鼻子】:ASCII Unicode UTF-8
【嵌牛正文】
一速妖、ASCII碼
我們知道虱颗,計(jì)算機(jī)內(nèi)部,所有信息最終都是一個(gè)二進(jìn)制值蔗喂。每一個(gè)二進(jìn)制位(bit)有 0 和 1 兩種狀態(tài)上枕,因此八個(gè)二進(jìn)制位就可以組合出256種狀態(tài),這被稱為一個(gè)字節(jié)(byte)弱恒。也就是說,一個(gè)字節(jié)一共可以用來(lái)表示256種不同的狀態(tài)棋恼,每一個(gè)狀態(tài)對(duì)應(yīng)一個(gè)符號(hào)返弹,就是256個(gè)符號(hào),從 00000000 到11111111爪飘。
上個(gè)世紀(jì)60年代义起,美國(guó)制定了一套字符編碼,對(duì)英語(yǔ)字符與二進(jìn)制位之間的關(guān)系师崎,做了統(tǒng)一規(guī)定默终。這被稱為 ASCII 碼,一直沿用至今犁罩。
ASCII 碼一共規(guī)定了128個(gè)字符的編碼齐蔽,比如空格 SPACE 是32(二進(jìn)制 00100000 ),大寫的字母A 是65(二進(jìn)制 01000001 )床估。這128個(gè)符號(hào)(包括32個(gè)不能打印出來(lái)的控制符號(hào))含滴,只占用了一個(gè)字節(jié)的后面7位,最前面的一位統(tǒng)一規(guī)定為 0 丐巫。
二谈况、非ASCII碼
英語(yǔ)用128個(gè)符號(hào)編碼就夠了,但是用來(lái)表示其他語(yǔ)言递胧,128個(gè)符號(hào)是不夠的碑韵。比如,在法語(yǔ)中缎脾,字母上方有注音符號(hào)祝闻,它就無(wú)法用 ASCII 碼表示。于是赊锚,一些歐洲國(guó)家就決定治筒,利用字節(jié)中閑置的最高位編入新的符號(hào)。比如舷蒲,法語(yǔ)中的 é 的編碼為130(二進(jìn)制 10000010 )耸袜。這樣一來(lái),這些歐洲國(guó)家使用的編碼體系牲平,可以表示最多256個(gè)符號(hào)堤框。
但是,這里又出現(xiàn)了新的問題。不同的國(guó)家有不同的字母蜈抓,因此启绰,哪怕它們都使用256個(gè)符號(hào)的編碼方式,代表的字母卻不一樣沟使。比如委可,130在法語(yǔ)編碼中代表了 é ,在希伯來(lái)語(yǔ)編碼中卻代表了字母Gimel ( ? )腊嗡,在俄語(yǔ)編碼中又會(huì)代表另一個(gè)符號(hào)着倾。但是不管怎樣,所有這些編碼方式中燕少,0--127表示的符號(hào)是一樣的卡者,不一樣的只是128--255的這一段。
至于亞洲國(guó)家的文字客们,使用的符號(hào)就更多了崇决,漢字就多達(dá)10萬(wàn)左右。一個(gè)字節(jié)只能表示256種符號(hào)底挫,肯定是不夠的恒傻,就必須使用多個(gè)字節(jié)表達(dá)一個(gè)符號(hào)。比如建邓,簡(jiǎn)體中文常見的編碼方式是
GB2312碌冶,使用兩個(gè)字節(jié)表示一個(gè)漢字,所以理論上最多可以表示 256 x 256 = 65536 個(gè)符號(hào)涝缝。
中文編碼的問題需要專文討論扑庞,這篇筆記不涉及。這里只指出拒逮,雖然都是用多個(gè)字節(jié)表示一個(gè)符號(hào)罐氨,但是GB類的漢字編碼與后文的 Unicode 和 UTF-8 是毫無(wú)關(guān)系的。
三滩援、Unicode
正如上一節(jié)所說栅隐,世界上存在著多種編碼方式,同一個(gè)二進(jìn)制數(shù)字可以被解釋成不同的符號(hào)玩徊。因此租悄,要想打開一個(gè)文本文件,就必須知道它的編碼方式恩袱,否則用錯(cuò)誤的編碼方式解讀泣棋,就會(huì)出現(xiàn)亂碼。為什么電子郵件常常出現(xiàn)亂碼畔塔?就是因?yàn)榘l(fā)信人和收信人使用的編碼方式不一樣潭辈。
可以想象鸯屿,如果有一種編碼,將世界上所有的符號(hào)都納入其中把敢。每一個(gè)符號(hào)都給予一個(gè)獨(dú)一無(wú)二的編碼寄摆,那么亂碼問題就會(huì)消失。這就是 Unicode修赞,就像它的名字都表示的婶恼,這是一種所有符號(hào)的編碼。
Unicode 當(dāng)然是一個(gè)很大的集合柏副,現(xiàn)在的規(guī)奈跷荆可以容納100多萬(wàn)個(gè)符號(hào)。每個(gè)符號(hào)的編碼都不一樣搓扯,比如, U+0639 表示阿拉伯字母 Ain 包归, U+0041 表示英語(yǔ)的大寫字母 A 锨推,U+4E25 表示漢字 嚴(yán) 。具體的符號(hào)對(duì)應(yīng)表公壤,可以查詢unicode.org换可,或者專門的漢字對(duì)應(yīng)表。
四厦幅、Unicode的問題
需要注意的是沾鳄,Unicode 只是一個(gè)符號(hào)集,它只規(guī)定了符號(hào)的二進(jìn)制代碼确憨,卻沒有規(guī)定這個(gè)二進(jìn)制代碼應(yīng)該如何存儲(chǔ)译荞。
比如,漢字 嚴(yán) 的 Unicode 是十六進(jìn)制數(shù) 4E25 休弃,轉(zhuǎn)換成二進(jìn)制數(shù)足足有15位( 100111000100101 )吞歼,也就是說,這個(gè)符號(hào)的表示至少需要2個(gè)字節(jié)塔猾。表示其他更大的符號(hào)篙骡,可能需要3個(gè)字節(jié)或者4個(gè)字節(jié),甚至更多丈甸。
這里就有兩個(gè)嚴(yán)重的問題糯俗,第一個(gè)問題是,如何才能區(qū)別 Unicode 和 ASCII 睦擂?計(jì)算機(jī)怎么知道三個(gè)字節(jié)表示一個(gè)符號(hào)得湘,而不是分別表示三個(gè)符號(hào)呢?第二個(gè)問題是顿仇,我們已經(jīng)知道忽刽,英文字母只用一個(gè)字節(jié)表示就夠了天揖,如果 Unicode 統(tǒng)一規(guī)定,每個(gè)符號(hào)用三個(gè)或四個(gè)字節(jié)表示跪帝,那么每個(gè)英文字母前都必然有二到三個(gè)字節(jié)是 0 今膊,這對(duì)于存儲(chǔ)來(lái)說是極大的浪費(fèi),文本文件的大小會(huì)因此大出二三倍伞剑,這是無(wú)法接受的斑唬。
它們?cè)斐傻慕Y(jié)果是:1)出現(xiàn)了 Unicode 的多種存儲(chǔ)方式,也就是說有許多種不同的二進(jìn)制格式黎泣,可以用來(lái)表示 Unicode恕刘。2)Unicode 在很長(zhǎng)一段時(shí)間內(nèi)無(wú)法推廣,直到互聯(lián)網(wǎng)的出現(xiàn)抒倚。
五褐着、UTF-8
互聯(lián)網(wǎng)的普及,強(qiáng)烈要求出現(xiàn)一種統(tǒng)一的編碼方式托呕。UTF-8 就是在互聯(lián)網(wǎng)上使用最廣的一種 Unicode 的實(shí)現(xiàn)方式含蓉。其他實(shí)現(xiàn)方式還包括 UTF-16(字符用兩個(gè)字節(jié)或四個(gè)字節(jié)表示)和 UTF-32(字符用四個(gè)字節(jié)表示),不過在互聯(lián)網(wǎng)上基本不用项郊。重復(fù)一遍馅扣,這里的關(guān)系是,UTF-8 是 Unicode 的實(shí)現(xiàn)方式之一着降。
UTF-8 最大的一個(gè)特點(diǎn)差油,就是它是一種變長(zhǎng)的編碼方式。它可以使用1~4個(gè)字節(jié)表示一個(gè)符號(hào)任洞,根據(jù)不同的符號(hào)而變化字節(jié)長(zhǎng)度蓄喇。
UTF-8 的編碼規(guī)則很簡(jiǎn)單,只有二條:
1)對(duì)于單字節(jié)的符號(hào)交掏,字節(jié)的第一位設(shè)為 0 公罕,后面7位為這個(gè)符號(hào)的 Unicode 碼。因此對(duì)于英語(yǔ)字母耀销,UTF-8 編碼和 ASCII 碼是相同的楼眷。
2)對(duì)于 n 字節(jié)的符號(hào)( n? >? 1 ),第一個(gè)字節(jié)的前 n 位都設(shè)為 1 熊尉,第 n? +? 1 位設(shè)為 0 罐柳,后面字節(jié)的前兩位一律設(shè)為 10 。剩下的沒有提及的二進(jìn)制位狰住,全部為這個(gè)符號(hào)的 Unicode 碼张吉。
下表總結(jié)了編碼規(guī)則,字母 x 表示可用編碼的位催植。
跟據(jù)上表肮蛹,解讀 UTF-8 編碼非常簡(jiǎn)單勺择。如果一個(gè)字節(jié)的第一位是 0 ,則這個(gè)字節(jié)單獨(dú)就是一個(gè)字符伦忠;如果第一位是 1 省核,則連續(xù)有多少個(gè) 1 ,就表示當(dāng)前字符占用多少個(gè)字節(jié)昆码。
下面气忠,還是以漢字 嚴(yán) 為例,演示如何實(shí)現(xiàn) UTF-8 編碼赋咽。
嚴(yán) 的 Unicode 是 4E25 ( 100111000100101 )旧噪,根據(jù)上表,可以發(fā)現(xiàn) 4E25 處在第三行的范圍內(nèi)( 0000 0800 - 0000 FFFF )脓匿,因此 嚴(yán) 的 UTF-8 編碼需要三個(gè)字節(jié)淘钟,即格式是1110xxxx 10xxxxxx 10xxxxxx 。然后陪毡,從 嚴(yán) 的最后一個(gè)二進(jìn)制位開始米母,依次從后向前填入格式中的 x ,多出的位補(bǔ) 0 缤骨。這樣就得到了,嚴(yán) 的 UTF-8 編碼是 11100100 10111000 10100101 尺借,轉(zhuǎn)換成十六進(jìn)制就是 E4B8A5 绊起。
六、Unicode與UTF-8之間的轉(zhuǎn)換
通過上一節(jié)的例子燎斩,可以看到 嚴(yán) 的 Unicode碼 是 4E25 虱歪,UTF-8 編碼是 E4B8A5 ,兩者是不一樣的栅表。它們之間的轉(zhuǎn)換可以通過程序?qū)崿F(xiàn)笋鄙。
Windows平臺(tái),有一個(gè)最簡(jiǎn)單的轉(zhuǎn)化方法怪瓶,就是使用內(nèi)置的記事本小程序 notepad.exe 萧落。打開文件后,點(diǎn)擊 文件 菜單中的 另存為 命令洗贰,會(huì)跳出一個(gè)對(duì)話框找岖,在最底部有一個(gè) 編碼 的下拉條。
里面有四個(gè)選項(xiàng): ANSI 敛滋, Unicode 许布, Unicode big endian 和 UTF-8 。
1)ANSI 是默認(rèn)的編碼方式绎晃。對(duì)于英文文件是 ASCII 編碼蜜唾,對(duì)于簡(jiǎn)體中文文件是? GB2312 編碼(只針對(duì) Windows 簡(jiǎn)體中文版杂曲,如果是繁體中文版會(huì)采用 Big5 碼)。
2)Unicode 編碼這里指的是 notepad.exe 使用的 UCS-2 編碼方式袁余,即直接用兩個(gè)字節(jié)存入字符的 Unicode 碼擎勘,這個(gè)選項(xiàng)用的 little endian 格式。
3)Unicode big endian 編碼與上一個(gè)選項(xiàng)相對(duì)應(yīng)泌霍。我在下一節(jié)會(huì)解釋 little endian 和 big endian 的涵義货抄。
4)UTF-8 編碼,也就是上一節(jié)談到的編碼方法朱转。
選擇完"編碼方式"后蟹地,點(diǎn)擊"保存"按鈕,文件的編碼方式就立刻轉(zhuǎn)換好了藤为。
七怪与、Little endian 和 Big endian
上一節(jié)已經(jīng)提到,UCS-2 格式可以存儲(chǔ) Unicode 碼(碼點(diǎn)不超過 0xFFFF )缅疟。以漢字 嚴(yán) 為例分别,Unicode 碼是 4E25 ,需要用兩個(gè)字節(jié)存儲(chǔ)存淫,一個(gè)字節(jié)是 4E 耘斩,另一個(gè)字節(jié)是 25 。存儲(chǔ)的時(shí)候桅咆, 4E 在前括授, 25 在后,這就是 Big endian 方式岩饼; 25 在前荚虚, 4E 在后,這是 Little endian 方式籍茧。
這兩個(gè)古怪的名稱來(lái)自英國(guó)作家斯威夫特的《格列佛游記》版述。在該書中,小人國(guó)里爆發(fā)了內(nèi)戰(zhàn)寞冯,戰(zhàn)爭(zhēng)起因是人們爭(zhēng)論渴析,吃雞蛋時(shí)究竟是從大頭(Big-endian)敲開還是從小頭(Little-endian)敲開。為了這件事情吮龄,前后爆發(fā)了六次戰(zhàn)爭(zhēng)檬某,一個(gè)皇帝送了命,另一個(gè)皇帝丟了王位螟蝙。
第一個(gè)字節(jié)在前恢恼,就是"大頭方式"(Big endian),第二個(gè)字節(jié)在前就是"小頭方式"(Little endian)胰默。
那么很自然的场斑,就會(huì)出現(xiàn)一個(gè)問題:計(jì)算機(jī)怎么知道某一個(gè)文件到底采用哪一種方式編碼漓踢?
Unicode 規(guī)范定義,每一個(gè)文件的最前面分別加入一個(gè)表示編碼順序的字符漏隐,這個(gè)字符的名字叫做"零寬度非換行空格"(zero width no-break space)喧半,用 FEFF 表示。這正好是兩個(gè)字節(jié)青责,而且 FF 比 FE 大 1 挺据。
如果一個(gè)文本文件的頭兩個(gè)字節(jié)是 FE FF ,就表示該文件采用大頭方式脖隶;如果頭兩個(gè)字節(jié)是 FF FE 扁耐,就表示該文件采用小頭方式。
八产阱、實(shí)例
下面婉称,舉一個(gè)實(shí)例。
打開"記事本"程序 notepad.exe 构蹬,新建一個(gè)文本文件王暗,內(nèi)容就是一個(gè) 嚴(yán) 字,依次采用ANSI庄敛,Unicode俗壹,Unicode big endian 和 UTF-8 編碼方式保存。
然后藻烤,用文本編輯軟件UltraEdit 中的"十六進(jìn)制功能"绷雏,觀察該文件的內(nèi)部編碼方式。
1)ANSI:文件的編碼就是兩個(gè)字節(jié) D1 CF 隐绵,這正是 嚴(yán) 的 GB2312 編碼之众,這也暗示 GB2312 是采用大頭方式存儲(chǔ)的拙毫。
2)Unicode:編碼是四個(gè)字節(jié) FF FE 25 4E 依许,其中 FF FE 表明是小頭方式存儲(chǔ),真正的編碼是 4E25缀蹄。
3)Unicode big endian:編碼是四個(gè)字節(jié) FE FF 4E 25 峭跳,其中 FE FF 表明是大頭方式存儲(chǔ)。
4)UTF-8:編碼是六個(gè)字節(jié) EF BB BF E4 B8 A5 缺前,前三個(gè)字節(jié) EF BB BF 表示這是UTF-8編碼蛀醉,后三個(gè) E4B8A5 就是 嚴(yán) 的具體編碼,它的存儲(chǔ)順序與編碼順序是一致的衅码。
九拯刁、延伸閱讀
The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets(關(guān)于字符集的最基本知識(shí))
RFC3629:UTF-8, a transformation format of ISO 10646(如果實(shí)現(xiàn)UTF-8的規(guī)定)