從0開(kāi)始學(xué)習(xí)字符編碼
有一些知識(shí)你無(wú)時(shí)無(wú)刻不在接觸,但是一旦讓你回答,可能除了它的名字就回答不上其他什么了,字符編碼就是這樣,大家都知道UTF-8和GBK,可能還會(huì)知道中文字符用GBK的比較多,但是UTF-8不是也支持中文字符嗎?為什么我們會(huì)用到這兩種編碼呢,為什么沒(méi)有統(tǒng)一使用一種?ASCII碼也是,最開(kāi)始計(jì)算機(jī)基礎(chǔ)就有學(xué)過(guò)它,但還是不知道它和其他編碼的關(guān)系。本節(jié),我們就詳細(xì)的說(shuō)一說(shuō),字符編碼。
?
一、ASCII碼
1.ASCII碼的誕生
首先,計(jì)算機(jī)最終是二進(jìn)制的,也就是讓它表達(dá)一下數(shù)字,可以用二進(jìn)制表達(dá)出來(lái)。但是如果讓它表達(dá)字符,就沒(méi)那么簡(jiǎn)單了,不同的字符之間毫無(wú)規(guī)律,所以只能用每個(gè)數(shù)字,代表一個(gè)獨(dú)一無(wú)二的字符,像元素周期表一樣,一對(duì)一。
ASCII碼就是這樣誕生的,在上世紀(jì)60年代,由美國(guó)制定的一套字符編碼。它一共定義了128個(gè)字符,從二進(jìn)制來(lái)說(shuō)就是7位,從000 0000到111 1111的128種組合方式,從十進(jìn)制說(shuō)就是0-127。
2.ASCII碼到底是幾位
根據(jù)前面的分析,ASCII碼應(yīng)該是7位的,它不需要用到第八位就已經(jīng)給所有符號(hào)用完了。英語(yǔ)就26個(gè)字母,老外能絞盡腦汁湊到128個(gè)字符出來(lái)已經(jīng)很努力了,你再讓它整128個(gè)出來(lái),這不是拿頭給你想嗎。所以最開(kāi)始它肯定是7位的,第八位有也是固定為0。
而現(xiàn)在很多說(shuō)法也有說(shuō)它是8位的,一方面是擴(kuò)展,不同地區(qū)可能會(huì)將ASCII碼剩下的128個(gè)位利用起來(lái),擴(kuò)展使用。這就導(dǎo)致不同國(guó)家地區(qū)的擴(kuò)展后的ASDII碼各不相同,當(dāng)然前128位是固定死的,后面的128位就差距很大了。又有些人說(shuō)計(jì)算機(jī)通常是用8位表示ASCII碼,因?yàn)榈?位是用來(lái)做奇偶校驗(yàn)的,既然有使用,所以是ASCII就是8位。
很多不同的理由,但個(gè)人感覺(jué)ASCII還是算7位的,擴(kuò)充后的ASCII碼自然也不是最原始的ASCII碼了,更何況還有那么多種不同的擴(kuò)充版本。
?
?
二、不同國(guó)家的編碼
擴(kuò)展ASCII碼只是第一步,像歐洲一些國(guó)家,使用的語(yǔ)言和英文差別不大,它們就可以把原本閑置的第八位用起來(lái),加上自己需要的一些符號(hào)進(jìn)去。當(dāng)然不同的國(guó)家肯定會(huì)有不同的語(yǔ)言符號(hào),甚至同一個(gè)符號(hào)有不同的含義和讀法,所以差異就產(chǎn)生了。
這些國(guó)家還好,如果是亞洲國(guó)家,那就更沒(méi)法表達(dá)了。不管是128還是256都是遠(yuǎn)遠(yuǎn)無(wú)法表達(dá)如此數(shù)量龐大的漢字的,所以我們漢字也創(chuàng)建了屬于自己的字符集:GB2312,它是中國(guó)國(guó)家標(biāo)準(zhǔn)的簡(jiǎn)體中文字符集,并且兼容ASCII碼。不同的國(guó)家基本上都有自己的本土化的字符集。
?
1.字符編碼
嚴(yán)格來(lái)說(shuō),字符集只是一種字符的集合,它和計(jì)算機(jī)是沒(méi)有直接關(guān)聯(lián)的。所以需要將字符進(jìn)行二進(jìn)制編碼,將不同的字符用特定的方式存儲(chǔ)到計(jì)算機(jī)中。
第一層編碼,我們需要給每個(gè)字符通過(guò)某種規(guī)則定下一個(gè)唯一對(duì)應(yīng)的數(shù)字,這是一對(duì)一的,例如ASCII碼中,"A"表示數(shù)字65。
第二層編碼,存儲(chǔ)長(zhǎng)度(字節(jié)數(shù)),是采用固定的存儲(chǔ)長(zhǎng)度,還是根據(jù)不同字符有不同變化的。例如ASCII碼就是用一個(gè)字節(jié)存儲(chǔ)的。
第三層編碼,存儲(chǔ)格式,大端法還是小端法,這一層通常影響不大,有時(shí)是由系統(tǒng)決定,例如X86的機(jī)器采用的是小端法。
2.字符集(charset)和字符編碼(encoding)
通過(guò)字符編碼的步驟,按理說(shuō)字符集和編碼方式應(yīng)當(dāng)是分開(kāi)的,字符集純放字符,然后編碼方式制定了123層的編碼規(guī)則。
但實(shí)際上并不是這樣,至少通過(guò)我的查閱,發(fā)現(xiàn)很多字符集默認(rèn)等于編碼方式,嚴(yán)格的說(shuō),至少也是和第一層編碼捆綁的。也就是在字符集發(fā)明的時(shí)候,也同時(shí)制定好了編碼規(guī)則,并沒(méi)有嚴(yán)格的區(qū)別字符集和字符編碼,界限比較模糊?;蛟S是因?yàn)樵谀莻€(gè)時(shí)間,每種字符集和它的編碼方式都是一對(duì)一的,且目的都是為了計(jì)算機(jī)編碼使用,所以沒(méi)有必要進(jìn)行區(qū)分吧。
就像現(xiàn)在我們的GB2312雖然是字符集,但是也同時(shí)是字符編碼。
直到Unicode的出現(xiàn),大家才有意識(shí)將字符集和編碼區(qū)分開(kāi)來(lái),因?yàn)閁nicode有了不同的編碼方式。
字符集:charset,即character set 的簡(jiǎn)寫(xiě)
字符集編碼:encoding,是 charset encoding 的簡(jiǎn)寫(xiě),通常直接簡(jiǎn)稱編碼
?
?
三、Unicode
1.Unicode的誕生
既然不同國(guó)家有了不同的字符集和編碼方式,同樣一句話,我用的字符集和編碼方式和你那邊的截然不同,那就肯定會(huì)產(chǎn)生問(wèn)題。例如我打了“你好”兩個(gè)字,在國(guó)內(nèi)自然大部分都是用相同的字符集和編碼方式的,所以互相發(fā)消息沒(méi)問(wèn)題。但如果跨了不同的國(guó)家,它那邊又沒(méi)有安裝你的字符集,怎么可能轉(zhuǎn)換出你的消息呢。萬(wàn)一同樣的編碼在它那邊的字符集和編碼后對(duì)應(yīng)的是“傻嗶”兩個(gè)字呢,那不是尷尬的一b(當(dāng)然很大概率都是轉(zhuǎn)成亂碼)。
但是,如果讓他那邊也自動(dòng)安裝你的字符集和編碼方式解決,就代表它也要安裝全世界每種字符集和編碼去兼容,顯然是增添了太多無(wú)意義的負(fù)擔(dān)。
上面的情況倒還是其次,更嚴(yán)重的是如果一篇文章涉及了兩種語(yǔ)言呢?難不成一半用A編碼一半用B編碼?
所以統(tǒng)一字符集Unicode就出世了,又名萬(wàn)國(guó)碼、統(tǒng)一碼,它收納了世界上的所有符號(hào),只要計(jì)算機(jī)支持它一種字符集,就能表達(dá)任何語(yǔ)言。
?
2.Unicode的問(wèn)題
Unicode誕生后,顯然它同時(shí)定義了前面所說(shuō)的第一層編碼,但是卻沒(méi)有定義第二層編碼,其實(shí)我在第五層。
也就是Unicode沒(méi)有定義該如何進(jìn)行編碼存儲(chǔ)進(jìn)計(jì)算機(jī),不像ASCII碼,直接就可以定位7位的長(zhǎng)度然后計(jì)算機(jī)8位一個(gè)字節(jié)存儲(chǔ)。Unicode的范圍太大了,如果都以最長(zhǎng)存儲(chǔ),兩個(gè)字節(jié)65536都不太夠用(據(jù)說(shuō)已經(jīng)差不多有10萬(wàn)個(gè)字符了),統(tǒng)一用三個(gè)字節(jié)又太過(guò)于浪費(fèi)了。如果你是個(gè)只需要打英語(yǔ)的,本來(lái)一個(gè)字節(jié)就能覆蓋所有英文字符了,結(jié)果Unicode硬是逼你三個(gè)字節(jié)存一個(gè)字符,那真是吃的太飽了。
如果說(shuō)用一個(gè)字節(jié)表示英文(ASCII碼),二到四個(gè)字節(jié)表示其他字符,那又會(huì)產(chǎn)生新的問(wèn)題,計(jì)算機(jī)要怎樣才能知道下一個(gè)字符是幾個(gè)字節(jié)的呢?例如接下來(lái)的連續(xù)三個(gè)字節(jié),是表示一個(gè)字符,還是三個(gè)單字節(jié)的字符呢?
加上由于當(dāng)時(shí)互聯(lián)網(wǎng)還沒(méi)出現(xiàn),Unicode也無(wú)法得到有效的推廣,沒(méi)有有效統(tǒng)一的解決方式,甚至出現(xiàn)了很多種不同的編碼存儲(chǔ)方式。
?
3.UTF-8
然后互聯(lián)網(wǎng)時(shí)代到來(lái)了,UTF-8就殺出來(lái)了,它是互聯(lián)網(wǎng)上使用最廣泛的一種Unicode編碼方式,和它相似的還有UTF-16和UTF-32。它們都是Unicode的不同的編碼方式,可以說(shuō)區(qū)別就是第二層編碼,但是顯然其他的編碼方式?jīng)]有UTF-8使用廣。所以這時(shí)候同一種字符集Unicode,有了不同的編碼方式,自然大家就開(kāi)始將字符集和字符編碼進(jìn)行了區(qū)分。
?
而UTF-8能從眾多編碼方式中脫穎而出,就在于它是可變長(zhǎng)度的,也就是不會(huì)造成過(guò)多的資源浪費(fèi),并且它的編碼規(guī)則也很簡(jiǎn)單:
- ①對(duì)于單字節(jié)的符號(hào),字節(jié)的第一位固定設(shè)為0,后面的7位是Unicode碼,這點(diǎn)兼容了ASCII碼。
- ②對(duì)于大于一個(gè)字節(jié)的n字節(jié)符號(hào),第一個(gè)字節(jié)的前n位固定設(shè)為1,第n+1位設(shè)為0,后面字節(jié)的前兩位一律設(shè)為10,剩下所有其他的位組合起來(lái)就是Unicode碼。
這兩條規(guī)則的目的就是解決如何區(qū)分這幾個(gè)字節(jié)到底是一個(gè)字符還是多個(gè)字符。
如果不太理解的話,可以換個(gè)思路去理解:
站在計(jì)算機(jī)的角度,進(jìn)行編碼時(shí),先讀取第一個(gè)字節(jié),查看它的首位,如果是0,說(shuō)明這個(gè)字節(jié)一定是一個(gè)單字節(jié)的字符,就直接解析它后面的7位進(jìn)行編碼即可。如果是1,就繼續(xù)讀取這個(gè)字節(jié)的第二位第三位,直到碰到0為止,如果第二位就是0了,說(shuō)明這個(gè)字節(jié)是前面字節(jié)的附屬。如果第三第四位或以后才是0,那么這個(gè)字節(jié)開(kāi)頭有幾個(gè)連續(xù)的1,就說(shuō)明這個(gè)字符將會(huì)有幾個(gè)字節(jié)。
這里給個(gè)表格參照一下,左邊是符號(hào)在Unicode中表示所需的位數(shù),右邊就是它的UTF-8編碼方式了,可以看到目前最多可以用四個(gè)字節(jié)表示一個(gè)字符
Unicode符號(hào)位數(shù) | UTF-8編碼方式
(二進(jìn)制) | (二進(jìn)制)
----------------------+---------------------------------------------
1到7位 | 0xxxxxxx
8到11位 | 110xxxxx 10xxxxxx
12到16位 | 1110xxxx 10xxxxxx 10xxxxxx
17到21位 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
舉例說(shuō)明:
例如中文字符【碼】,它的Unicode碼是<font color=red>111 1000 0000 0001</font>,共15位,對(duì)照上圖就是第三行,需要三個(gè)字節(jié)去表示,那么它的UTF-8碼就是1110<font color=red>0111</font> 10<font color=red>100000</font> 10<font color=red>000001</font>,理解了嗎,將紅字提出來(lái)組合在一起就是Unicode碼,所以盡管是同一個(gè)字符,它的Unicode碼和UTF-8碼是不同的,注意這點(diǎn)哦。
有些人可能不知道怎么獲取一個(gè)字符的Unicode碼和UTF-8碼的二進(jìn)制,我這里簡(jiǎn)單寫(xiě)了個(gè)java測(cè)試類,供參考:
public static void main(String[] args) throws UnsupportedEncodingException {
char a = '碼';
//輸出2進(jìn)制的Unicode碼
System.out.println("二進(jìn)制的Unicode碼:"+Integer.toBinaryString(a));
//輸出16進(jìn)制的Unicode碼
System.out.println("十六進(jìn)制的Unicode碼:"+Integer.toHexString(a));
//獲取到了字符UTF-8的byte數(shù)組,byte也是十進(jìn)制數(shù)字的存儲(chǔ)格式,所以要轉(zhuǎn)換輸出
byte[] aBytes = String.valueOf(a).getBytes("UTF-8");
String aBinary;
System.out.println("二進(jìn)制的UTF-8碼:");
for (byte aByte : aBytes) {
//直接輸出aByte的話,會(huì)得到三個(gè)-127~127之間的整數(shù),所以要作轉(zhuǎn)換處理
//由于是補(bǔ)碼,所以先處理為原碼,再利用Integer的方法輸出二進(jìn)制
aBinary = Integer.toBinaryString(aByte & 0xFF);
System.out.print(aBinary+" ");
}
//附送一個(gè)16進(jìn)制的UTF8碼
String a16= URLEncoder.encode(String.valueOf(a), "UTF-8");
//由于是百分號(hào)分隔,有點(diǎn)丑,我們換個(gè)符號(hào)
a16 = a16.replace("%", "-");
System.out.println("\n十六進(jìn)制的UTF-8碼:"+a16);
}
另外,其實(shí)網(wǎng)上大多數(shù)人討論這些碼時(shí),大多是用16進(jìn)制表示的,例如什么4EA2。但我在看的時(shí)候看的有點(diǎn)暈,本身編碼這回事就有點(diǎn)亂,還要在二進(jìn)制、十六進(jìn)制、十進(jìn)制之間轉(zhuǎn)換來(lái)轉(zhuǎn)換去,實(shí)在太繞了,所以我這里都是直接用二進(jìn)制的。所以如果大家看到十六進(jìn)制的也無(wú)需擔(dān)心,轉(zhuǎn)換為二進(jìn)制后其實(shí)都一樣。
關(guān)于UTF-8的最大字節(jié)數(shù):
其實(shí)在早期,一開(kāi)始UTF-8設(shè)計(jì)的最大字節(jié)數(shù)是6個(gè)字節(jié),也就是它最多可能會(huì)用6個(gè)字節(jié)表示一個(gè)字符。但是后面重新規(guī)范后,只能使用Unicode定義的區(qū)域 U+0000到U+10FFFF,所以通常我們說(shuō)UTF-8最大是4個(gè)字節(jié)。
所以如果有人說(shuō)UTF-8是6個(gè)字節(jié),也并非空穴來(lái)風(fēng)。只是現(xiàn)如今我們世界所有的符號(hào)加起來(lái)的數(shù)量,都沒(méi)有達(dá)到能使用5個(gè)字節(jié)的UTF-8的程度,并且大概率以后也會(huì)一直是最大4個(gè)字節(jié)。
?
4.為什么還需要GBK?
按理說(shuō)編碼統(tǒng)一了,那就都用Unicode(UTF-8)不就行了,但是我們會(huì)發(fā)現(xiàn)日常使用中,還是有很多地方是GBK的,新建一個(gè)txt,默認(rèn)的文本存儲(chǔ)格式也是GBK的,這是為什么?
雖然UTF-8是可變長(zhǎng)的,但并不代表它沒(méi)有浪費(fèi)資源(只是浪費(fèi)的少了),到了四個(gè)字節(jié),差不多11個(gè)位是固定寫(xiě)死的,都用來(lái)識(shí)別了,顯然這就是一種浪費(fèi),也是可變長(zhǎng)度需要付出的一定代價(jià)。在UTF-8中漢字通常是三個(gè)字節(jié),這樣我們一篇文章就至少得用三個(gè)字節(jié)去存儲(chǔ)一個(gè)文字。
而GBK是專門(mén)用于中國(guó)的,基本上我們常用的任何字符,甚至附近幾個(gè)亞洲國(guó)家的字符(日文、俄文、韓文)都囊括在內(nèi),最重要的是它表示一個(gè)漢字只需要兩個(gè)字節(jié)存儲(chǔ),顯然比UTF-8要節(jié)省很多空間,文本少的時(shí)候可能沒(méi)有感覺(jué)到,但一旦文本多了,差異是十分明顯的。
?
我們本地也可以簡(jiǎn)單的證明這點(diǎn):
①在任意位置,鼠標(biāo)右鍵=》新建=》文本文檔
-
②隨便打一萬(wàn)個(gè)漢字,點(diǎn)另存為,編碼選擇ANSI,在國(guó)內(nèi)ANSI編碼就是GBK編碼的意思,原因后面會(huì)說(shuō)明
保存為ANSI編碼 ③繼續(xù)另存為,這次編碼選擇UTF-8再保存

下圖可以看到,兩個(gè)文件的大小,GBK只需要2萬(wàn)個(gè)字節(jié)存儲(chǔ),而UTF-8則需要3萬(wàn)個(gè)字節(jié),整整多了一半的存儲(chǔ)資源,而文本卻是一毛一樣的!

不光是中國(guó),其他國(guó)家恐怕也會(huì)這樣,特別是亞洲的一些國(guó)家。由于主要語(yǔ)言不是英文字母,可能都會(huì)有自己的字符集和編碼方式。如果平時(shí)的使用確定不會(huì)涉及國(guó)際化,一般也會(huì)選用自己國(guó)家的定義的編碼方式,方便,也節(jié)省資源。
當(dāng)然最主要的還是windows默認(rèn)為不同的地區(qū)選擇了默認(rèn)的編碼方式。不過(guò)如果當(dāng)初windows鐵了心無(wú)論什么地區(qū)都一律使用UTF-8,說(shuō)不定現(xiàn)在整個(gè)計(jì)算機(jī)網(wǎng)絡(luò)環(huán)境會(huì)很少出現(xiàn)亂碼問(wèn)題。
但是這些決斷現(xiàn)在也不用打馬后炮了,因?yàn)橐殉啥ň至恕?/p>
?
?
四、中國(guó)的編碼歷史
前面我們簡(jiǎn)單的提到了一下GB2312和GBK,相信大家都看出來(lái)這兩個(gè)都是中國(guó)的編碼標(biāo)準(zhǔn)了,當(dāng)然現(xiàn)在基本上都是用GBK了。這里我就簡(jiǎn)單的概況一下我們國(guó)家自己的編碼標(biāo)準(zhǔn)的發(fā)展史,雖然取名差異很大,但是都是GB打頭的,是“國(guó)標(biāo)”兩個(gè)字的簡(jiǎn)寫(xiě),所以能從這判斷出是中國(guó)的編碼。
實(shí)際上這段發(fā)展史遠(yuǎn)比我這幾段字復(fù)雜嚴(yán)謹(jǐn)?shù)亩?,有興趣的同學(xué)可以自己去查閱一下,我這只能通過(guò)網(wǎng)上的一些資料,簡(jiǎn)單且不嚴(yán)謹(jǐn)?shù)目偨Y(jié)那么一小下。
1.GB2312字符集
最開(kāi)始便是GB2312,是一個(gè)簡(jiǎn)體中文字符集的中國(guó)國(guó)家標(biāo)準(zhǔn),沒(méi)有區(qū)分編碼。前面127個(gè)字符仍然保留了ASCII碼,后面就是漢字了,存儲(chǔ)是一個(gè)漢字用兩個(gè)字節(jié)。
它一共收錄了6763個(gè)常用漢字,也收錄了包括拉丁字母、希臘字母、日文平片假名、俄語(yǔ)西里爾字母在內(nèi)的682個(gè)全角字符。兩個(gè)字節(jié)長(zhǎng)的就是全角字符,而原來(lái)在127號(hào)以下的那些就叫半角字符了。
附加思考:全角和半角標(biāo)點(diǎn)符號(hào)
相信從使用輸入法接觸計(jì)算機(jī)以來(lái),我們就知道全角字符和半角字符的存在了,可能還知道半角字符就是英文的符號(hào),全角是中文的符號(hào)。并且我們?cè)诖虼a時(shí),標(biāo)點(diǎn)符號(hào)都得用半角的。例如逗號(hào),代碼語(yǔ)法里用的都是半角逗號(hào):【,】,如果不小心哪里用了全角的逗號(hào):【,】,就會(huì)編譯異常。
這說(shuō)明半角和全角的標(biāo)點(diǎn)符號(hào)是完全不一樣的字符,其實(shí)全角的標(biāo)點(diǎn)就是中文編碼里新定義的字符,我們可以理解為它已經(jīng)是一種特殊的漢字了,而半角的符號(hào)就是ASCII碼里前128個(gè)符號(hào)。
既然是不同的符號(hào),不同的字符,也就有不同的字符編號(hào),當(dāng)然都是不兼容的。所以中文逗號(hào)和英文逗號(hào)注定是沒(méi)有關(guān)聯(lián)的,最多只能抽象的說(shuō)它們的意思是一樣的。
?
2.GB13000
GB13000的出現(xiàn)主要是為了便于多種文字同時(shí)處理,它包含了20902個(gè)漢字,顯然比GB2312擴(kuò)充了很多,并定義了一套全新的編碼體系。但是吧,好像沒(méi)火起來(lái),說(shuō)實(shí)話我網(wǎng)上都搜不到太多它的資料,差點(diǎn)就以為GB2312后面直接就是GBK了。估計(jì)可以說(shuō)它雖然制定好了,但是實(shí)現(xiàn)起來(lái)出現(xiàn)了困難。
?
3.GBK
網(wǎng)上有種說(shuō)法是GBK最初是微軟推出對(duì)GB2312的擴(kuò)展,在windows95簡(jiǎn)中版使用。隨著它的流行和廣泛使用,國(guó)家就正式將其確定為官方的了,不過(guò)這種說(shuō)法也倒是沒(méi)有找到具體的證明。唯一能確定的是,GBK并不是國(guó)家標(biāo)準(zhǔn),只是作為技術(shù)規(guī)范指導(dǎo)性文件?,F(xiàn)在不管它的起源是如何,它現(xiàn)在也是國(guó)內(nèi)windows系統(tǒng)最常用的一種編碼方式了。
GBK擴(kuò)充于GB2312,支持GB13000。從原來(lái)GB2312擴(kuò)充至21003個(gè)漢字(包括繁體)和883個(gè)符號(hào),遠(yuǎn)大于原來(lái)的6000多個(gè),并且編碼方式也改變了。雖然它收錄了GB13000的所有漢字,但是它的編碼方式和GB13000制定的不同,所以只能算是從GB2312到GB13000的一種過(guò)渡方案。(當(dāng)然,到現(xiàn)在似乎也沒(méi)能過(guò)渡成功,反而都用GBK了)
?
4.GB18030
GB18030又是對(duì)GBK的一次擴(kuò)充,所以它兼容GBK和GB2312,這次它收錄了國(guó)內(nèi)少數(shù)民族的字符,說(shuō)明少數(shù)民族村里也開(kāi)始通網(wǎng)了,同時(shí)增補(bǔ)了一些韓文字符,和一些雜七雜八的??偣彩珍浟?7484個(gè)漢字,覆蓋了東亞地區(qū)的大部分內(nèi)容。并且它也整了個(gè)可變長(zhǎng)的編碼方式,有單字節(jié)、雙字節(jié)、四字節(jié)三種。
目前它是我們的國(guó)家標(biāo)準(zhǔn),意思就是強(qiáng)制性的。只是在windows系統(tǒng)中還沒(méi)有推廣開(kāi)來(lái),網(wǎng)上搜了一些資料,看來(lái)是任重而道遠(yuǎn)啊。。。但是這些東西就交給專業(yè)的人忙活吧。
目前windows中還是常用GBK的,但是別忘記,我國(guó)的編碼國(guó)家標(biāo)準(zhǔn)還是GB18030,GBK只能說(shuō)算是一個(gè)行業(yè)規(guī)范。并且和GB13000不同,GB18030在系統(tǒng)中是有這種具體的編碼方式的。例如我最常用的IDEA,里面是可以切換文件為GB18030的編碼的。
?
5.GB編碼總結(jié)
可以看到,基本上中文編碼,現(xiàn)在就是GB2312、GBK、GB18030這三種。其實(shí)除了我說(shuō)的這些還有很多GB打頭的字符集,只是我對(duì)此了解也不是很多,畢竟也就是個(gè)小小碼農(nóng),不需要在這種地方太鉆牛角尖,只是沒(méi)想到GBK的發(fā)展有這么多故事。說(shuō)不準(zhǔn)以后哪天標(biāo)配就是GB18030了,所以還是應(yīng)該有所了解的。
?
?
*五、其他的幾個(gè)編碼集(選讀)
感覺(jué)說(shuō)都說(shuō)了好幾種了,其他的也粗略的說(shuō)一兩句吧,不然好像差了點(diǎn)什么。。但是這些編碼既不是全球統(tǒng)一的,也不是國(guó)內(nèi)通用的,所以簡(jiǎn)單看看就行了,跳過(guò)不看也沒(méi)啥影響。
1.ISO-8859
ISO8859不是一個(gè)標(biāo)準(zhǔn),是一系列的標(biāo)準(zhǔn),所以我們會(huì)看到ISO-8859-1、ISO-8859-2、ISO-8859-3這些不同的字符集。它下面這些不同的字符集使用了不同國(guó)家地區(qū)的字符,也就是給不同國(guó)家使用的,相互之間相差甚遠(yuǎn)。
并且都是單字節(jié)的字符集,其實(shí)就是利用了ASCII沒(méi)有定義的第8位,添加一些字符,128之后留了32個(gè)碼位給擴(kuò)充定義的32個(gè)控制碼,所以它新增的字符的范圍只有0xA1---0xFF(161---255),歐洲那邊用的多,或者說(shuō)自己國(guó)家字符少的國(guó)家可以用(中國(guó)肯定用不了)。
碼位:
有時(shí)候我們會(huì)看到碼位、編號(hào)、區(qū)位,其實(shí)都差不多,都是對(duì)字符集中某個(gè)字符的位置的形容,也就是一個(gè)座位號(hào)。并且通常是用十六進(jìn)制表示,如0xA1之類的。
?
2.ISO-2022
顯然ISO8859只能在給那些拉丁語(yǔ)系或字符少的國(guó)家使用,對(duì)于中日韓,肯定是沒(méi)辦法的。ISO2022就是這樣出來(lái)的,它和8859一樣代表的是一系列的標(biāo)準(zhǔn),像ISO-2022-CN、ISO-2022-JP、ISO-2022-KR代表的就是中日韓的標(biāo)準(zhǔn)。
它也是可變長(zhǎng)的編碼方式,且支持GB2312,不過(guò)這里就不細(xì)說(shuō)原理了,反正基本沒(méi)啥人會(huì)用。
?
附加知識(shí):ISO是什么組織
ISO是國(guó)際標(biāo)準(zhǔn)化組織International Organization for Standardization,它并不只為字符集服務(wù),它是專門(mén)用來(lái)制定一些全球化標(biāo)準(zhǔn)的組織,涉及目前的絕大多數(shù)領(lǐng)域。所以Unicode字符集這種全球統(tǒng)一的字符集,也有它的一份力。
?
3.BIG5
大五碼是彎彎(中國(guó)臺(tái)灣?。┠沁呌玫木幋a,共收錄13,053個(gè)字,基本上就都是繁體字,局限性很大。不說(shuō)不常見(jiàn)的字了,就連一些常見(jiàn)的字符都沒(méi)收錄全。
不過(guò)上世紀(jì)90年代,香港也開(kāi)始用了,畢竟它們都是用繁體的。然后香港發(fā)現(xiàn)有很多他們需要的字都沒(méi)收錄后,自然也一直在為缺少的字忙活??,還出了個(gè)香港增補(bǔ)字符集,用來(lái)解決這個(gè)問(wèn)題。
?
4.UTF-16和UTF-32
UTF-8前面說(shuō)完了,這里再簡(jiǎn)單說(shuō)說(shuō)它兩個(gè)沒(méi)什么人氣的兄弟:UTF-16和UTF-32。
①UTF-32
先說(shuō)說(shuō)UTF-32,和最開(kāi)始的ASCII碼一樣,是固定長(zhǎng)度的,32位即4字節(jié)。基本上沒(méi)有4字節(jié)表達(dá)不了的符號(hào)了,但是同時(shí)也是個(gè)浪費(fèi)空間的敗家子,你想想你打個(gè)hello,用UTF-8也就5個(gè)字節(jié),UTF-32要給你用掉20個(gè)字節(jié),刺不刺激?
所以這也側(cè)面說(shuō)明了UTF-8這種可變長(zhǎng)的編碼的意義。
另外由于它是固定長(zhǎng)度的,并且又不是單字節(jié),處理單元是四個(gè)字節(jié)。所以會(huì)衍生出一種問(wèn)題,就是大小端存儲(chǔ),不同的系統(tǒng)可能是不一樣的,它怎么知道順序是從大到小還是從小到大呢?
所以編碼時(shí)就會(huì)分UTF-32 BE(Big Endian)和UTF-32 LE(Little Endian),直接從這就可以區(qū)分了。
什么是大端小端:
直接用例子說(shuō)明比較簡(jiǎn)單,假設(shè)現(xiàn)在有一個(gè)字符,它是用4個(gè)字節(jié)表示的:0x12345678(這是16位表示,兩個(gè)數(shù)字是一個(gè)字節(jié)),那么
大端法:
低地址 -----------------> 高地址
0x12 | 0x34 | 0x56 | 0x78小端法:
低地址 ------------------> 高地址
0x78 | 0x56 | 0x34 | 0x12就是一種順序的不同
?
②UTF-16
UTF-16是變長(zhǎng)的,只不過(guò)它只有兩種變體:2字節(jié)和4字節(jié)。并且大部分都屬于2字節(jié),據(jù)說(shuō)一開(kāi)始就是固定2字節(jié)存儲(chǔ)的,16位=2字節(jié)嘛,但是顯然碼位不夠,就想辦法延長(zhǎng)♂到4字節(jié)了。
所以對(duì)于編號(hào)在 U+0000 到 U+FFFF 的字符(常用字符集),直接用兩個(gè)字節(jié)表示。
而編號(hào)在 U+10000 到 U+10FFFF 之間的字符,則需要用四個(gè)字節(jié)表示。
另外它也會(huì)有大小端的問(wèn)題,所以會(huì)看到UTF-16 BE 和UTF-16 LE兩種。
?
?
六、其他一些問(wèn)題
列一些和字符有關(guān)的其他問(wèn)題,可能會(huì)有人需要一個(gè)答案,后續(xù)可能會(huì)更新補(bǔ)充這一塊。
?
1.什么是ANSI?
記事本保存時(shí),默認(rèn)就是ANSI的編碼方式,當(dāng)然現(xiàn)在我們知道了它是GBK,那么ANSI=GBK嗎?
其實(shí)不是,ANSI編碼的意思可以理解為“本地編碼”,也就是在中國(guó)它代表的是GBK,在臺(tái)灣省代表的是Big5,在日本就代表的是JIS,好一個(gè)墻頭草。所以無(wú)需理解的太復(fù)雜,在中國(guó)的windows,你就當(dāng)它是GBK就行了。
所以注意,ANSI和ASCII是沒(méi)啥關(guān)系的,除了長(zhǎng)的是有那么一點(diǎn)點(diǎn)像。
?
2.什么是UCS,它和Unicode有什么聯(lián)系?
①什么是UCS:
有時(shí)我們會(huì)看到所謂的UCS字符集,實(shí)際上它的全名是Information technology -- Universal Coded Character Set (UCS) 【翻譯:信息技術(shù)通用編碼字符集(UCS)】,用編號(hào)來(lái)表示就是ISO 10646標(biāo)準(zhǔn),其實(shí)它才是ISO組織出來(lái)為了統(tǒng)一全球字符編碼的。注意它剛出來(lái)的時(shí)候和Unicode是不同的,Unicode是一個(gè)名為Unicode聯(lián)盟的學(xué)術(shù)學(xué)會(huì)的機(jī)構(gòu)創(chuàng)建的,也就是當(dāng)時(shí)UCS和Unicode都是懷揣著同一目的誕生的:統(tǒng)一全球的編碼。
②它和Unicode的聯(lián)系
后來(lái),顯然世界上不需要兩種不同的統(tǒng)一編碼,所以他們進(jìn)行了合作,從Unicode2.0開(kāi)始,兩家基本上就都差不多了,而UCS-2和UCS-4就是UCS的具體編碼方式,和UTF-8對(duì)于Unicode一樣。
并且,UCS-2可以看作是早期UTF-16的父集,都是固定用2個(gè)字節(jié)表示的,只是后面UTF-16引入了輔助平面字符,可以用4個(gè)字節(jié)表示字符了,就無(wú)法相提并論了。
UCS-4則和UTF-32基本沒(méi)什么區(qū)別,至少,目前是差不多的。
?
?
?
參考資料:
1.百度百科
2.字符編碼筆記:ASCII,Unicode 和 UTF-8:【推薦閱讀,雖然是07年的文章,但是仍然值得一看】
http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
3.字符集與編碼(一)——charset vs encoding:【這是個(gè)系列,一共有九篇,可以都閱讀一下】
https://my.oschina.net/goldenshaw/blog/304493?fromerr=aRaz3kw3
4.編碼方式的比較,以及UTF-8,gb2312的選擇:
https://www.cnblogs.com/shanwater/p/5616589.html
5.GB2312、GB 13000、GBK、GB18030 介紹和說(shuō)明文檔:
https://wenku.baidu.com/view/05703370f524ccbff021843b.html
6.ANSI是什么編碼?:
