在說常見的字符編碼(ASCII、gb2312、gbk、utf-8等)之前,我們先來看看編碼的歷史吧。 編碼編年史
ASCII編碼 ASCII(American Standard Code for Information Interchange,美國信息交換標(biāo)準(zhǔn)代碼)是基于拉丁字母的一套電腦編碼系統(tǒng)。它主要用于顯示現(xiàn)代英語,而其擴(kuò)展版本EASCII則可以部分支持其他西歐語言,并等同于國際標(biāo)準(zhǔn)ISO/IEC 646。 ASCII第一次以規(guī)范標(biāo)準(zhǔn)的型態(tài)發(fā)表是在1967年,最后一次更新則是在1986年,至今為止共定義了128個字符;其中33個字符無法顯示(一些終端提供了擴(kuò)展,使得這些字符可顯示為諸如笑臉、撲克牌花式等8-bit符號),且這33個字符多數(shù)都已是陳廢的控制字符??刂谱址挠猛局饕怯脕聿倏匾呀?jīng)處理過的文字。在33個字符之外的是95個可顯示的字符,包含用鍵盤敲下空白鍵所產(chǎn)生的空白字符也算1個可顯示字符(顯示為空白)。 非ASCII編碼 英語用128個符號編碼就夠了,但是用來表示其他語言,128個符號是不夠的,所以就出現(xiàn)了很多非ASCII編碼。 GB 2312 定義 GB 2312 或 GB 2312–80 是中華人民共和國國家標(biāo)準(zhǔn)簡體中文字符集,全稱《信息交換用漢字編碼字符集·基本集》,又稱GB0,由中國國家標(biāo)準(zhǔn)總局發(fā)布,1981年5月1日實施。GB 2312編碼通行于中國大陸;新加坡等地也采用此編碼。中國大陸幾乎所有的中文系統(tǒng)和國際化的軟件都支持GB 2312。 概述 GB 2312標(biāo)準(zhǔn)共收錄6763個漢字,其中一級漢字3755個,二級漢字3008個;同時收錄了包括拉丁字母、希臘字母、日文平假名及片假名字母、俄語西里爾字母在內(nèi)的682個字符。 GB 2312的出現(xiàn),基本滿足了漢字的計算機(jī)處理需要,它所收錄的漢字已經(jīng)覆蓋中國大陸99.75%的使用頻率。但對于人名、古漢語等方面出現(xiàn)的罕用字和繁體字,GB 2312不能處理,因此后來GBK及GB 18030漢字字符集相繼出現(xiàn)以解決這些問題。 在使用GB 2312的程序通常采用EUC(EUC全名為Extended Unix Code,是一個使用8位編碼來表示字符的方法。)儲存方法,以便兼容于ASCII。瀏覽器編碼表上的“GB2312”,通常都是指“EUC-CN”表示法。 分區(qū)表示 GB 2312中對所收漢字進(jìn)行了“分區(qū)”處理,每區(qū)含有94個漢字/符號。這種表示方式也稱為區(qū)位碼。 01–09區(qū)為特殊符號。 16–55區(qū)為一級漢字,按拼音排序。 56–87區(qū)為二級漢字,按部首/筆畫排序。 舉例來說,“啊”字是GB 2312之中的第一個漢字,它的區(qū)位碼就是1601。 10–15區(qū)及88–94區(qū)則未有編碼。但在附錄3,則在第10區(qū)推薦作為 GB 1988–80 中的94個圖形字符區(qū)域(即第3區(qū)字符之半形版本)。 字節(jié)結(jié)構(gòu) 每個漢字及符號以兩個字節(jié)來表示。第一個字節(jié)稱為“高位字節(jié)”,第二個字節(jié)稱為“低位字節(jié)”。 “高位字節(jié)”使用了0xA1–0xF7(把01–87區(qū)的區(qū)號加上0xA0),“低位字節(jié)”使用了0xA1–0xFE(把01–94加上0xA0)。由于一級漢字從16區(qū)起始,漢字區(qū)的“高位字節(jié)”的范圍是0xB0–0xF7,“低位字節(jié)”的范圍是0xA1–0xFE,占用的碼位是72*94=6768。其中有5個空位是D7FA–D7FE。 例如“啊”字在大多數(shù)程序中,會以兩個字節(jié),0xB0(第一個字節(jié))0xA1(第二個字節(jié))儲存。(與區(qū)位碼對比:0xB0=0xA0 16,0xA1=0xA0 1)。 有兩種不同的GB 2312實現(xiàn) 有兩種不同的GB 2312實現(xiàn),在它們之間存在少量的差別,其中至少有一個是錯誤的。
實現(xiàn)A與GBK/GB 18030兼容,實現(xiàn)B則不兼容。 在2015年,微軟.Net Framework在使用實現(xiàn)A。 iconv-1.14, php-5.6, ActivePerl-5.20, Java 1.7, Python 3.4在使用實現(xiàn)B.[2] Ruby 2.2則同時兼容實現(xiàn)A和實現(xiàn)B,對這幾個有沖突的字符,它在內(nèi)部轉(zhuǎn)換為實現(xiàn)A。 GBK編碼 漢字內(nèi)碼擴(kuò)展規(guī)范,稱GBK,全名為《漢字內(nèi)碼擴(kuò)展規(guī)范(GBK)》1.0版,由中華人民共和國全國信息技術(shù)標(biāo)準(zhǔn)化技術(shù)委員會1995年12月1日制訂,國家技術(shù)監(jiān)督局標(biāo)準(zhǔn)化司和電子工業(yè)部科技與質(zhì)量監(jiān)督司1995年12月15日聯(lián)合以《技術(shù)標(biāo)函[1995]229號》文件的形式公布。 GBK的K為漢語拼音Kuo Zhan(擴(kuò)展)中“擴(kuò)”字的聲母。英文全稱Chinese Internal Code Extension Specification。 GBK 只為“技術(shù)規(guī)范指導(dǎo)性文件”,不屬于國家標(biāo)準(zhǔn)。國家質(zhì)量技術(shù)監(jiān)督局于2000年3月17日推出了GB 18030-2000標(biāo)準(zhǔn),以取代GBK。 Unicode編碼 unicode(中文:萬國碼、國際碼、統(tǒng)一碼、單一碼)是計算機(jī)科學(xué)領(lǐng)域里的一項業(yè)界標(biāo)準(zhǔn)。它對世界上大部分的文字系統(tǒng)進(jìn)行了整理、編碼,使得電腦可以用更為簡單的方式來呈現(xiàn)和處理文字。 Unicode伴隨著通用字符集的標(biāo)準(zhǔn)而發(fā)展,同時也以書本的形式[1]對外發(fā)表。Unicode至今仍在不斷增修,每個新版本都加入更多新的字符。目前最新的版本為2016年6月21日公布的9.0.0[2],已經(jīng)收入超過十萬個字符(第十萬個字符在2005年獲采納)。Unicode涵蓋的數(shù)據(jù)除了視覺上的字形、編碼方法、標(biāo)準(zhǔn)的字符編碼外,還包含了字符特性,如大小寫字母。 Unicode發(fā)展由非營利機(jī)構(gòu)統(tǒng)一碼聯(lián)盟負(fù)責(zé),該機(jī)構(gòu)致力于讓Unicode方案替換既有的字符編碼方案。因為既有的方案往往空間非常有限,亦不適用于多語環(huán)境。 Unicode備受認(rèn)可,并廣泛地應(yīng)用于電腦軟件的國際化與本地化過程。有很多新科技,如可擴(kuò)展置標(biāo)語言、Java編程語言以及現(xiàn)代的操作系統(tǒng),都采用Unicode編碼。 需要注意的是,Unicode只是一個符號集,它只規(guī)定了符號的二進(jìn)制代碼,卻沒有規(guī)定這個二進(jìn)制代碼應(yīng)該如何存儲。 比如,漢字“嚴(yán)”的unicode是十六進(jìn)制數(shù)4E25,轉(zhuǎn)換成二進(jìn)制數(shù)足足有15位(100111000100101),也就是說這個符號的表示至少需要2個字節(jié)。表示其他更大的符號,可能需要3個字節(jié)或者4個字節(jié),甚至更多。 這里就有兩個嚴(yán)重的問題,第一個問題是,如何才能區(qū)別unicode和ascii?計算機(jī)怎么知道三個字節(jié)表示一個符號,而不是分別表示三個符號呢?第二個問題是,我們已經(jīng)知道,英文字母只用一個字節(jié)表示就夠了,如果unicode統(tǒng)一規(guī)定,每個符號用三個或四個字節(jié)表示,那么每個英文字母前都必然有二到三個字節(jié)是0,這對于存儲來說是極大的浪費,文本文件的大小會因此大出二三倍,這是無法接受的。 它們造成的結(jié)果是:1)出現(xiàn)了unicode的多種存儲方式,也就是說有許多種不同的二進(jìn)制格式,可以用來表示unicode。2)unicode在很長一段時間內(nèi)無法推廣,直到互聯(lián)網(wǎng)的出現(xiàn)。 Unicode的實現(xiàn)方式 Unicode的實現(xiàn)方式不同于編碼方式。一個字符的Unicode編碼是確定的。但是在實際傳輸過程中,由于不同系統(tǒng)平臺的設(shè)計不一定一致,以及出于節(jié)省空間的目的,對Unicode編碼的實現(xiàn)方式有所不同。Unicode的實現(xiàn)方式稱為Unicode轉(zhuǎn)換格式(Unicode Transformation Format,簡稱為UTF)。 UTF-8 互聯(lián)網(wǎng)的普及,強(qiáng)烈要求出現(xiàn)一種統(tǒng)一的編碼方式。UTF-8就是在互聯(lián)網(wǎng)上使用最廣的一種unicode的實現(xiàn)方式。其他實現(xiàn)方式還包括UTF-16和UTF-32,不過在互聯(lián)網(wǎng)上基本不用。重復(fù)一遍,這里的關(guān)系是,UTF-8是Unicode的實現(xiàn)方式之一。 UTF-8最大的一個特點,就是它是一種變長的編碼方式。它可以使用1~4個字節(jié)表示一個符號,根據(jù)不同的符號而變化字節(jié)長度。 UTF-8的編碼規(guī)則很簡單,只有二條: 1)對于單字節(jié)的符號,字節(jié)的第一位設(shè)為0,后面7位為這個符號的unicode碼。因此對于英語字母,UTF-8編碼和ASCII碼是相同的。 2)對于n字節(jié)的符號(n>1),第一個字節(jié)的前n位都設(shè)為1,第n 1位設(shè)為0,后面字節(jié)的前兩位一律設(shè)為10。剩下的沒有提及的二進(jìn)制位,全部為這個符號的unicode碼。 下表總結(jié)了編碼規(guī)則,字母x表示可用編碼的位。
Unicode 和 UTF-8 之間的轉(zhuǎn)換關(guān)系表 ( x 字符表示碼點占據(jù)的位 ) UTF-16 UTF-16是Unicode字符編碼五層次模型的第三層:字符編碼表(Character Encoding Form,也稱為 'storage format')的一種實現(xiàn)方式。即把Unicode字符集的抽象碼位映射為16位長的整數(shù)(即碼元)的序列,用于數(shù)據(jù)存儲或傳遞。Unicode字符的碼位,需要1個或者2個16位長的碼元來表示,因此這是一個變長表示。 UTF-16比起UTF-8,好處在于大部分字符都以固定長度的字節(jié) (2字節(jié)) 儲存,但UTF-16卻無法兼容于ASCII編碼。 UTF-32 UTF-32 (或 UCS-4)是一種將Unicode字符編碼的協(xié)議,對每一個Unicode碼位使用恰好32比特。UTF 只有此一種定長編碼。 UTF-32 使用32-比特的碼值,只在0到10FFFF的字碼空間(百萬個碼位)。 UTF-32 原本是一個 UCS-4 的子集,于是就現(xiàn)狀而言,除了 UTF-32 標(biāo)準(zhǔn)包含額外的 Unicode 意涵,UCS-4 和 UTF-32 大體是相同的。 小端和大端問題 Little endian和Big endian 上一節(jié)已經(jīng)提到,Unicode碼可以采用UCS-2格式直接存儲。以漢字”嚴(yán)“為例,Unicode碼是4E25,需要用兩個字節(jié)存儲,一個字節(jié)是4E,另一個字節(jié)是25。存儲的時候,4E在前,25在后,就是Big endian方式;25在前,4E在后,就是Little endian方式。 這兩個古怪的名稱來自英國作家斯威夫特的《格列佛游記》。在該書中,小人國里爆發(fā)了內(nèi)戰(zhàn),戰(zhàn)爭起因是人們爭論,吃雞蛋時究竟是從大頭(Big-Endian)敲開還是從小頭(Little-Endian)敲開。為了這件事情,前后爆發(fā)了六次戰(zhàn)爭,一個皇帝送了命,另一個皇帝丟了王位。 因此,第一個字節(jié)在前,就是”大頭方式“(Big endian),第二個字節(jié)在前就是”小頭方式“(Little endian)。 那么很自然的,就會出現(xiàn)一個問題:計算機(jī)怎么知道某一個文件到底采用哪一種方式編碼? Unicode規(guī)范中定義,每一個文件的最前面分別加入一個表示編碼順序的字符,這個字符的名字叫做”零寬度非換行空格“(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。這正好是兩個字節(jié),而且FF比FE大1。 如果一個文本文件的頭兩個字節(jié)是FE FF,就表示該文件采用大頭方式;如果頭兩個字節(jié)是FF FE,就表示該文件采用小頭方式。 Base64 Base64是一種基于64個可打印字符來表示二進(jìn)制數(shù)據(jù)的表示方法。由于2的6次方等于64,所以每6個比特為一個單元,對應(yīng)某個可打印字符。三個字節(jié)有24個比特,對應(yīng)于4個Base64單元,即3個字節(jié)需要用4個可打印字符來表示。 在Base64中的可打印字符包括字母A-Z、a-z、數(shù)字0-9,這樣共有62個字符,此外兩個可打印符號在不同的系統(tǒng)中而不同。 完整的base64定義可見RFC 1421和RFC 2045。編碼后的數(shù)據(jù)比原始數(shù)據(jù)略長,為原來的4/3。 轉(zhuǎn)換過程: 轉(zhuǎn)換的時候,將三個byte的數(shù)據(jù),先后放入一個24bit的緩沖區(qū)中,先來的byte占高位。數(shù)據(jù)不足3byte的話,于緩沖器中剩下的bit用0補足。然后,每次取出6(因為26=64)個bit,按照其值選擇ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 /中的字符作為編碼后的輸出。不斷進(jìn)行,直到全部輸入數(shù)據(jù)轉(zhuǎn)換完成。 當(dāng)原數(shù)據(jù)長度不是3的整數(shù)倍時, 如果最后剩下一個輸入數(shù)據(jù),在編碼結(jié)果后加2個“=”;如果最后剩下兩個輸入數(shù)據(jù),編碼結(jié)果后加1個“=”;如果沒有剩下任何數(shù)據(jù),就什么都不要加,這樣才可以保證數(shù)據(jù)還原的正確性。(如果要編碼的字節(jié)數(shù)不能被3整除,最后會多出1個或2個字節(jié),那么可以使用下面的方法進(jìn)行處理:先使用0字節(jié)值在末尾補足,使其能夠被3整除,然后再進(jìn)行base64的編碼。在編碼后的base64文本后加上一個或兩個'='號,代表補足的字節(jié)數(shù)。也就是說,當(dāng)最后剩余一個八位字節(jié)(一個byte)時,最后一個6位的base64字節(jié)塊有四位是0值,最后附加上兩個等號;如果最后剩余兩個八位字節(jié)(2個byte)時,最后一個6位的base字節(jié)塊有兩位是0值,最后附加一個等號。參考下表:) Base64編碼的作用 由于某些系統(tǒng)中只能使用ASCII字符。Base64就是用來將非ASCII字符的數(shù)據(jù)轉(zhuǎn)換成ASCII字符的一種方法。它使用下面表中所使用的字符與編碼。 比如:GB2312-根據(jù)Base64規(guī)則->轉(zhuǎn)換成ASCII碼, 接收端收到ASCII碼-根據(jù)Base64規(guī)則->還原到GB2312 Base64常用于在通常處理文本數(shù)據(jù)的場合,表示、傳輸、存儲一些二進(jìn)制數(shù)據(jù)。包括MIME的email、在XML中存儲復(fù)雜數(shù)據(jù)。而且base64特別適合在http,mime協(xié)議下快速傳輸數(shù)據(jù)。 總結(jié) 關(guān)于編碼和解碼一定要對應(yīng),否則會產(chǎn)生亂碼。 |
|