一次性搞懂字符集,編碼,Unicode,Utf-8/16,BOM...

什么是編碼

眾所周知,計(jì)算機(jī)采用二進(jìn)制的計(jì)算方式,一個(gè)字節(jié)占8位(為什么是8位不是9位10位?大家可以自行探討),不管任何數(shù)據(jù),圖片,文字,聲音等等,都要轉(zhuǎn)換成具體的數(shù)值,最終轉(zhuǎn)換成二進(jìn)制數(shù)據(jù),計(jì)算機(jī)才能處理,這個(gè)過(guò)程就是編碼,本文所說(shuō)的編碼只是針對(duì)文字編碼的介紹。

AscII碼

現(xiàn)代計(jì)算機(jī)技術(shù)起源于美國(guó),編碼自然也是起源于美國(guó),AscII(American Standard Code for Information Interchange)碼就是第一套通用的計(jì)算機(jī)編碼,它包含了英文字符,阿拉伯?dāng)?shù)字和一些常用符號(hào),一共128個(gè),占一個(gè)字節(jié),第一位補(bǔ)0,例如常用的大寫字母A是65(01000001),小寫字母a是97,所以在JAVA里我們可以這樣進(jìn)行大小寫轉(zhuǎn)換:

    char a = 'a';
    char b = (char) (a-32);
    System.out.println(b);

字符集和編碼

從字面上來(lái)理解,字符集是一個(gè)包含指定字符的集合,而編碼是把文字轉(zhuǎn)換成對(duì)應(yīng)的數(shù)字碼,確實(shí)也是這樣,像AscII碼,他包含了128個(gè)特定字符,并且為每個(gè)字符指定了一個(gè)序號(hào),可以稱之為字符集,編碼則是像查字典一樣,從字符集中查到對(duì)應(yīng)的序號(hào),再把序號(hào)按編碼規(guī)則轉(zhuǎn)換成具體的數(shù)值,稱之為編碼。AscII碼比較簡(jiǎn)單,只占了一個(gè)字節(jié),他的編碼結(jié)果和碼表的序號(hào)值是一樣的,所以它的字符集和編碼的概念區(qū)分并不嚴(yán)格。

GB2312/GBK

隨著計(jì)算機(jī)技術(shù)的發(fā)展,AscII碼只能用于拉丁文字符的限制,嚴(yán)重影響了非英語(yǔ)系國(guó)家的使用,所以,各個(gè)國(guó)家和地區(qū)分別發(fā)展出了對(duì)應(yīng)本地語(yǔ)言的編碼方式,GB2312就是中國(guó)國(guó)家標(biāo)準(zhǔn)總局制定的漢字字符集標(biāo)準(zhǔn)碼,GB是國(guó)標(biāo)的意思。實(shí)際上,GB2312除了中文以外,還包含了拉丁字符,希臘字符,日文,俄語(yǔ)等等常見字符。

GB2312占據(jù)兩個(gè)字節(jié),也就是說(shuō)他能容納的字符數(shù)量為 216一共65536個(gè)。為了便于管理,GB2312將所有字符分成了若干個(gè)96字符的組,每個(gè)組稱為區(qū),字符所在的位置稱為區(qū)位,這樣,每個(gè)字符都有了對(duì)應(yīng)的區(qū)位碼。例如,漢字"安"所在的區(qū)為第16區(qū),位置序號(hào)是18,他的區(qū)位碼就是1618,有了區(qū)位碼以后怎樣才能轉(zhuǎn)換成編碼呢? 很簡(jiǎn)單,把區(qū)位碼分開,區(qū)號(hào)和位置序號(hào)分別加上 0xA0,就是對(duì)應(yīng)的編碼了。所以"安"的GB2312編碼就是 0xA0+16 0xA0+18 = 0xB0B2。

到這里,GB2312基本介紹完了,但是還有一個(gè)問(wèn)題,AscII碼在GB2312里是怎么處理的?實(shí)際上,GB2312兼容了AscII碼,上邊提到了,GB2312編碼是區(qū)位碼分別加A0得到的,A的編碼是1010,也就是說(shuō),GB2312的編碼必定是1開頭的,而AscII碼則是0開頭的,這樣就簡(jiǎn)單了,遇到1開頭的編碼,則按GB2312解碼,遇到0開頭的則按AscII解碼。

GBK是GB2312的升級(jí)版,不止包含常用漢字,還包含了繁體,部首,生僻字等等,幾乎可以滿足所有使用漢字的場(chǎng)景。

Unicode

Unicode是國(guó)際編碼組織制定的可以容納世界上所有文字和符號(hào)的字符編碼方案,范圍從0 - 0x10FFFF,分為17組,每個(gè)組稱為一個(gè)平面(plane),每個(gè)平面包含65536個(gè)字符,實(shí)際上目前只用到了少數(shù)平面,比如最常用的基本平面(BMP),范圍從0-0xFFFF.其他的從 0x010000 - 0x10FFFFFF 則稱為輔助平面(SMP),實(shí)際上在我們的日常使用中BMP基本上能滿足所有要求。

Unicode包含了全世界日常使用的幾乎所有字符,例如漢字"安",他的Unicode碼是0x5B89。

utf-8/16/32

什么是utf編碼?上邊介紹了Unicode,它是一個(gè)字符集,規(guī)定了每個(gè)字符對(duì)應(yīng)的數(shù)值,utf則是具體的實(shí)現(xiàn)方式,用各種方法來(lái)存儲(chǔ)字符對(duì)應(yīng)的數(shù)值,而根據(jù)各種實(shí)現(xiàn)方式的不同,又分為utf-8/16/32。

utf8是變長(zhǎng)編碼,如果是1字節(jié),它和Ascall碼是一樣的,多字節(jié)時(shí)第一字節(jié)的第一位開始填1,有幾個(gè)1表示占幾個(gè)字節(jié),比如110x xxxx表示兩個(gè)字節(jié),第二個(gè)字節(jié)開始一直到結(jié)束,都是10開頭,完整的編碼如下:

2字節(jié): 110x xxxx 10xx xxxx
3字節(jié): 1110 xxxx 10xx xxxx 10xx xxxx

同樣拿漢字"安"來(lái)說(shuō),它的Unicode碼是0x5B89,轉(zhuǎn)換成2進(jìn)制填充到utf-8的空位里去,多余的位置補(bǔ)0得到結(jié)果:0xE5AE89。

5B89
轉(zhuǎn)換成2進(jìn)制:          0101    101110    001001
填充到utf8格式中:1110 0101 10 101110 10 001001
得到utf8碼:    E5AE89

現(xiàn)在我們?cè)倏匆幌伦址途幋a,就比較清楚了,Unicode是字符集,utf-8編碼則是該字符集的一種實(shí)現(xiàn)方式,他們并不是同一個(gè)概念。

再來(lái)看一下utf-16編碼,utf-16的長(zhǎng)度固定為2個(gè)或者4個(gè)字節(jié),通常我們使用2個(gè)字節(jié)就可以了,它對(duì)應(yīng)的就是Unicode中的基本平面BMP,編碼就是Unicode碼,不足2個(gè)字節(jié)的位數(shù)在前邊補(bǔ)0,同樣是漢字"安",它的Unicode碼是0x5B89,所以u(píng)tf-16碼也是0x5B89。所以AscII碼也會(huì)占據(jù)兩個(gè)字節(jié),會(huì)有一部分空間浪費(fèi)。編碼Unicode輔助平面的字符時(shí),utf-16占用4個(gè)字節(jié),平時(shí)幾乎用不到,所以具體的編碼規(guī)則就不做介紹了。

utf-32編碼固定占用4個(gè)字節(jié),他對(duì)應(yīng)的是全部Unicode碼,這種編碼方式基本用不到,也不需要深入了解。

BOM,LE,BE

這幾個(gè)詞平時(shí)出現(xiàn)的頻率比較少一些,LE表示Little Endian,小端序,BE表示Big Endian,大端序,分別代表了計(jì)算機(jī)領(lǐng)域數(shù)據(jù)存儲(chǔ)的兩種方式。

大端序:

    數(shù)值:   0x12345678
    內(nèi)存低位地址(起始):  12  
                         34
                         56
    內(nèi)存高位地址(結(jié)束): 78

小端序:

    數(shù)值:   0x12345678
    內(nèi)存低位地址(起始):  78  
                         56
                         34
    內(nèi)存高位地址(結(jié)束): 12

實(shí)際上我們?cè)谌粘J褂眠^(guò)程中不需要關(guān)心當(dāng)前計(jì)算機(jī)使用的是那種方式。但在UTF-16編碼時(shí),它也有大小端序兩種編碼方式,例如0x5E89,大端序值為0x5E89,小端序則要反過(guò)來(lái)表示為0x895E,為了區(qū)分這兩種方式,在采用UTF-16編碼時(shí),在數(shù)據(jù)流的開始添加了一個(gè)統(tǒng)一的標(biāo)識(shí),0xFEFF表示大端序,對(duì)應(yīng)的編碼是utf-16be,0xFFFE表示小端序,對(duì)應(yīng)的編碼是utf-16be,這個(gè)表示就是BOM。

BOM全稱Byte Order Mark,字節(jié)序標(biāo)記,除了utf-16之外,utf-8也可以添加bom,它的bom固定為0xEFBBBF,選擇編碼方式為utf-8 with bom時(shí),生成的文件流中就會(huì)出現(xiàn)這個(gè)bom。為什么utf-8可以不需要bom呢,因?yàn)閡tf8是變長(zhǎng)的,它根據(jù)第一個(gè)字節(jié)信息判斷每個(gè)字符的長(zhǎng)度,不存在正反順序的問(wèn)題,我們?nèi)粘J褂玫膗tf-8都是不帶bom的。

Java里的編碼

看完了字符編碼的介紹,這里有一個(gè)問(wèn)題,java里的char類型能不能存儲(chǔ)中文字符?

java里char占了兩個(gè)字符,很容易得出結(jié)論,如果采用utf-8,是不能存儲(chǔ)中文的,其他編碼方式GB2312,utf-16都可以存儲(chǔ)中文,是這樣嗎?

其實(shí)沒(méi)有這么簡(jiǎn)單,不管我們?cè)诰庉嬈骼镞x什么編碼方式,對(duì)java里的char都沒(méi)有影響,實(shí)際上char對(duì)應(yīng)的是Unicode的基本平面BMP。我們?cè)跒g覽器里選擇的編碼只是對(duì)應(yīng)文件的保存方式,跟char采用什么編碼沒(méi)有任何關(guān)系,同樣的,如果我們?cè)趎ew一個(gè)String的時(shí)候指定了編碼,這個(gè)編碼也只對(duì)這一個(gè)String生效,而Java里的char是編譯器里定死了的,它對(duì)應(yīng)的就是BMP,也可以認(rèn)為是utf-16的2字節(jié)部分。

那么ava里的char類型到底能不能存儲(chǔ)中文字符,當(dāng)然可以,但是僅對(duì)應(yīng)BMP部分,各種繁體字生僻字就無(wú)能為力了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 騰訊大講堂——字符編碼的前世今生字符串,那些你不知道的事編碼字符集標(biāo)準(zhǔn)及分類研究通信用語(yǔ)の基礎(chǔ)知識(shí) —— ISO/...
    AItsuki閱讀 1,551評(píng)論 0 4
  • 1. ASCII ??我們知道,計(jì)算機(jī)內(nèi)部是通過(guò)二進(jìn)制數(shù)據(jù)進(jìn)行操作的,所有的信息最終都會(huì)轉(zhuǎn)換為一個(gè)二進(jìn)制值,二進(jìn)制...
    騎著烏龜去看海閱讀 1,846評(píng)論 0 4
  • 簡(jiǎn)單來(lái)說(shuō),unicode,gbk和大五碼就是編碼的值,而utf-8,uft-16之類就是這個(gè)值的表現(xiàn)形式.而前面那...
    百里求一閱讀 1,386評(píng)論 0 2
  • 我們經(jīng)常需要知道某個(gè)軟件版本是否安裝和版本號(hào)是多少,常常用到的命令是rpm -qa | grep 軟件名稱,但這并...
    robot_test_boy閱讀 2,540評(píng)論 3 0
  • 上午家里來(lái)朋友過(guò)來(lái),我?guī)麄內(nèi)コ粤嗽顼垼酝晡覀儨?zhǔn)備去江邊玩耍,經(jīng)過(guò)小區(qū)大門口的時(shí)候,看到了逗逗,我說(shuō):“哇撒寶貝...
    逗逗的媽媽姚蘭閱讀 246評(píng)論 0 0

友情鏈接更多精彩內(nèi)容