這篇文章主要介紹了下面三個問題:
1、什么是字符、字符集、字符編碼,他們的區(qū)別區(qū)別是什么?
2、什么是UTF-8、UTF-16、UTF-32,他們的區(qū)別是什么?
3、什么是GB2312、GBK、GB18030,他們的區(qū)別是什么?
?編碼基本概念
1. 字符
字符(Character):在計算機和電信技術(shù)中,一個字符是一個單位的字形、類字形單位或符號的基本信息。說的簡單點字符是各種文字和符號的總稱。一個字符可以是一個中文漢字、一個英文字母、一個阿拉伯數(shù)字、一個標點符號、一個圖形符號或者控制符號等。
2. 字符集
字符集(Character Set):是指多個字符的集合。不同的字符集包含的字符個數(shù)不一樣、包含的字符不一樣、對字符的編碼方式也不一樣。例如GB2312是中國國家標準的簡體中文字符集,GB2312收錄簡化漢字(6763個)及一般符號、序號、數(shù)字、拉丁字母、日文假名、希臘字母、俄文字母、漢語拼音符號、漢語注音字母,共 7445 個圖形字符。而ASCII字符集只包含了128字符,這個字符集收錄的主要字符是英文字母、阿拉伯字母和一些簡單的控制字符。
另外,還有其他常用的字符集有 GBK字符集、GB18030字符集、Big5字符集、Unicode字符集等。
3. 字符編碼
字符編碼(Character Encoding):字符編碼是指一種映射規(guī)則,根據(jù)這個映射規(guī)則可以將某個字符映射成其他形式的數(shù)據(jù)以便在計算機中存儲和傳輸。例如ASCII字符編碼規(guī)定使用單字節(jié)中低位的7個比特去編碼所有的字符,在這個編碼規(guī)則下字母A的編號是65(ASCII碼),用單字節(jié)表示就是0x41,因此寫入存儲設(shè)備的時候就是二進制的 01000001。每種字符集都有自己的字符編碼規(guī)則,常用的字符集編碼規(guī)則還有 UTF-8編碼、GBK編碼、Big5編碼等。
4. 碼點
碼點(Code Point):有些地方翻譯為碼值或內(nèi)碼。是指在某個字符集中,根據(jù)某種編碼規(guī)則將字符編碼后得到的值。比如在ASCII字符集中,字母A經(jīng)過ASCII編碼得到的值是65,那么65就是字符A在ASCII字符集中的碼點。
總結(jié):通俗解釋字符集就是把字符放到一起的一個集合。而這個集合的每一個字符都對應(yīng)一個數(shù)字,叫做碼點。那么,這樣就建立起來數(shù)字和字符之間的索引關(guān)系。那么,某個字符在計算機中怎么表示,具體占用幾個字節(jié)等等,這些就需要編碼規(guī)則來解決了。這個就是字符編碼,他來解決根據(jù)某個規(guī)則來將字符映射到相應(yīng)的碼點上面。
ASCII字符集
上個世紀60年代,美國制定了一套字符編碼規(guī)則,對英語字符與二進制位之間的關(guān)系做了統(tǒng)一規(guī)定,這編碼規(guī)則被稱為ASCII編碼,一直沿用至今。
ASCII編碼一共規(guī)定了128個字符的編碼規(guī)則,這128個字符形成的集合就叫做ASCII字符集。在ASCII編碼中,每個字符占用一個字節(jié)的后面7位,最前面的1位統(tǒng)一規(guī)定為0。在ASCII編碼中,0~31 是控制字符如換行回車刪除等,32~126 是可打印字符,可以通過鍵盤輸入并且能夠顯示出來。(下圖是ASCII字符集中字符和碼值的對應(yīng)關(guān)系)

英語用128個符號編碼就夠了,但是用來表示其他語言,128個符號是不夠的。所以當ASCII碼到歐洲的時候,一些歐洲國家就決定對ASCII編碼進行適當?shù)摹案脑臁保豪米止?jié)中閑置的最高位編入新的符號。比如,法語中的é的編碼為130(二進制10000010)。這樣一來,這些歐洲國家使用的編碼體系,可以表示最多256個符號。這個編碼統(tǒng)稱為EASCII(Extended ASCII)。
但是歐洲的語言體系有個特點:小國家特別多,每個國家可能都有自己的語言體系,語言環(huán)境十分復(fù)雜。因此即使EASCII可以表示256個字符,也不能統(tǒng)一歐洲的語言環(huán)境。
為了解決上面這個問題,人們想出了一個折中的方案:在EASCII中表示的256個字符中,前128字符和ASCII編碼表示的字符完全一樣,后128個字符每個國家或地區(qū)都有自己的編碼標準。比如,130在法語編碼中代表了é,在希伯來語編碼中卻代表了字母Gimel (?),在俄語編碼中又會代表另一個符號。但是不管怎樣,所有這些編碼方式中,0—127表示的符號是一樣的,不一樣的只是128—255的這一段。
根據(jù)這個規(guī)則,就形成了很多子標準:ISO-8859-1、ISO-8859-2、ISO-8859-3、……、ISO-8859-16。這些子標準適用于歐洲不同的國家地區(qū)。具體關(guān)于ISO-8859的標準請參考這個鏈接。這邊我摘錄了部分介紹。
ISO8859-1 字符集,也就是 Latin-1,是西歐常用字符,包括德法兩國的字母。
ISO8859-2 字符集,也稱為 Latin-2,收集了東歐字符。
ISO8859-3 字符集,也稱為 Latin-3,收集了南歐字符。
至于亞洲國家的文字,使用的符號就更多了,漢字就多達10萬左右。一個字節(jié)最多只能表示256種符號,肯定是不夠的,必須使用多個字節(jié)表達一個符號,因此才出現(xiàn)了后面的Unicode字符集和GB2312等字符集。比如,簡體中文常見的編碼方式是GB2312,使用兩個字節(jié)表示一個漢字,所以理論上最多可以表示65536個符號。
總結(jié):ASCII碼是美國制定的一套字符集,里面包含128個字符(2^7)所有只用了7位,最后一位默認賦值0,但是在一些歐洲國家為了滿足特殊符號需求,就把最后一位也用上了就多出來了128個字符(2^8)。個人理解:ASCII碼應(yīng)該只是字符集,而不是字符編碼,或者曾經(jīng)是字符編碼,現(xiàn)在大家都是廣義上面說ASCII編碼。
Unicode字符集
ASCII碼字符集,總共才能容納256個字符,對于全世界各國語言來說,很難全部包含在內(nèi),所有后來就出現(xiàn)了Unicode字符集。
Unicode字符集是一個很大的字符集合,現(xiàn)在的規(guī)模可以容納100多萬個符號。每個符號的編碼都不一樣,比如,U+0639表示阿拉伯字母Ain,U+0041表示英語的大寫字母A,U+4E25表示漢字“嚴”。
需要注意的是,Unicode只是一個字符集,它只規(guī)定了符號的二進制代碼,卻沒有規(guī)定這個二進制代碼應(yīng)該如何編碼如何存儲。這就造成了兩個問題:
第一個問題是,如何才能區(qū)別Unicode和ASCII?計算機怎么知道三個字節(jié)表示一個符號,而不是分別表示三個符號呢?
第二個問題是,我們已經(jīng)知道,英文字母只用一個字節(jié)表示就夠了,如果unicode統(tǒng)一規(guī)定,每個符號用三個或四個字節(jié)表示,那么每個英文字母前都必然有二到三個字節(jié)是0,這對于存儲來說是極大的浪費,文本文件的大小會因此大出二三倍,這是無法接受的。
為了解決Unicode字符集存在的問題,就出現(xiàn)了UTF(Unicode Transformation Formats)系列的編碼規(guī)則。UTF編碼規(guī)則具體規(guī)定了Unicode字符集中的字符是如何編碼的。
總結(jié):Unicode是一個很大的字符集,這個字符集只規(guī)定了這個字符集中每個字符對應(yīng)的碼值是多少,但是這個字符集并沒有規(guī)定具體的編碼規(guī)則,具體的編碼規(guī)則有UTF系列的編碼規(guī)則實現(xiàn)。
下面我們就來看看UTF系列編碼的具體實現(xiàn)。
UTF-8編碼
互聯(lián)網(wǎ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(8-bit Unicode Transformation Format)是一種針對Unicode的可變長度字符編碼規(guī)則,又稱萬國碼。由Ken Thompson于1992年創(chuàng)建?,F(xiàn)在已經(jīng)標準化為RFC 3629。UTF-8用1到4個字節(jié)編碼Unicode字符。用在網(wǎng)頁上可以統(tǒng)一頁面顯示中文簡體繁體及其它語言(如英文,日文,韓文)。

UTF-8最大的一個特點,就是它是一種變長的編碼方式。它可以使用1~4個字節(jié)表示一個符號,根據(jù)不同的符號而變化字節(jié)長度(UTF-8編碼可以容納2^21個字符,總共200多萬個字符)。
UTF-8的編碼規(guī)則很簡單,只有二條:
對于單字節(jié)的符號,字節(jié)的第一位設(shè)為0,后面7位為這個符號的unicode碼。因此對于英語字母,UTF-8編碼和ASCII碼是相同的。
對于n字節(jié)的符號(n>1),第一個字節(jié)的前n位都設(shè)為1,第n+1位設(shè)為0,后面字節(jié)的前兩位一律設(shè)為10。剩下的沒有提及的二進制位,全部為這個符號的unicode碼。
下表總結(jié)了編碼規(guī)則,字母x表示可用編碼的位。
Unicode符號范圍 | UTF-8編碼方式
UTF字節(jié)數(shù)???(十六進制) | (二進制)
一個字節(jié) 0000 0000-0000 007F | 0xxxxxxx
兩個字節(jié) 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
三個字節(jié) 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
四個字節(jié) 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
下面, 還是以漢字“嚴”為例,演示如何實現(xiàn)UTF-8編碼。
已知“嚴”的unicode是\u4E25(100111000100101),根據(jù)上表,可以發(fā)現(xiàn)4E25處在第三行的范圍內(nèi)(0000 0800-0000 FFFF),因此“嚴”的UTF-8編碼需要三個字節(jié),即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然后,從“嚴”的最后一個二進制位開始,依次從后向前填入格式中的x,多出的位補0。這樣就得到了,“嚴”的UTF-8編碼是“11100100 10111000 10100101”,轉(zhuǎn)換成十六進制就是E4B8A5。
UTF8、UTF16和UTF32之間的區(qū)別
再次強調(diào)一個概念:就是Unicode是一個字符集,這個字符集世界上所有的字符定義了一個唯一編碼。其僅僅規(guī)定了每個符號的二進制代碼,沒有制定細化的存儲規(guī)則。UTF-8、UTF-16、UTF-32才是Unicode的存儲格式定義。上面已經(jīng)簡單介紹了UTF8編碼規(guī)則,那么這個規(guī)則和UTF16、UTF32等規(guī)則有什么區(qū)別呢?
UCS-2和UCS-4
在將UTF8和UTF16、UTF32的區(qū)別之前,再先科普兩個名詞:UCS-2和UCS-4。
Unicode是為整合全世界的所有語言文字而誕生的。任何文字在Unicode中都對應(yīng)一個值,?這個值稱為代碼點(code point,也稱碼值)。代碼點的值通常寫成 U+ABCD 的格式。而文字和代碼點之間的對應(yīng)關(guān)系就是UCS-2(Universal Character Set coded in 2 octets)。顧名思義,UCS-2是用兩個字節(jié)來表示代碼點,其取值范圍為 U+0000~U+FFFF。
為了能表示更多的文字,人們又提出了UCS-4,即用四個字節(jié)表示代碼點。它的范圍為 U+00000000~U+7FFFFFFF,其中 U+00000000~U+0000FFFF和UCS-2是一樣的。
要注意,UCS-2和UCS-4只規(guī)定了代碼點和文字之間的對應(yīng)關(guān)系,并沒有規(guī)定代碼點在計算機中如何存儲。規(guī)定存儲方式的稱為UTF(Unicode Transformation Format),也就是我們上面提到的UTF8格式和下面將要提到的UTF16、UTF32格式。
UTF-16編碼格式
UTF-16由RFC2781規(guī)定,它使用兩個字節(jié)來表示一個代碼點。不難猜到,UTF-16是完全對應(yīng)于UCS-2的,即把UCS-2規(guī)定的代碼點通過Big Endian或Little Endian方式直接保存下來。UTF-16包括三種:UTF-16,UTF-16BE(Big Endian)和UTF-16LE(Little Endian)。UTF-16BE和UTF-16LE不難理解,而UTF-16就需要通過在文件開頭以名為BOM(Byte Order Mark)的字符來表明文件是Big Endian還是Little Endian。BOM為U+FEFF這個字符。其實BOM是個小聰明的想法。由于UCS-2沒有定義U+FEFF,因此只要出現(xiàn) FF FE 或者 FE FF 這樣的字節(jié)序列,就可以認為它是U+FEFF,并且可以判斷出是Big Endian還是Little Endian。
BOM(Byte Order Mark)用來放在文檔的開頭告訴閱讀器該文檔的字節(jié)序。UTF-8不需要BOM來表明字節(jié)順序,但可以用BOM來表明編碼方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8編碼是EF BB BF。所以如果接收者收到以EF BB BF開頭的字節(jié)流,就知道這是UTF-8編碼了。UTF-16才需要加BOM。因為它是按Unicode 順序編碼,在BMP范圍內(nèi)是二字節(jié),需要識別是大或小字節(jié)序。
這邊順便科普下大小端的概念。
低字節(jié)序(Little Endian)和高字節(jié)序(Big Endian)
低字節(jié)序和高字節(jié)序只是一個關(guān)于在內(nèi)存中存儲和讀取一段字節(jié)(被稱作words)的約定。這意味著當你讓計算機用UTF-16把字母A(占兩個字節(jié))存在內(nèi)存中時,使用哪種字節(jié)序方案決定了你把第一個字節(jié)放在第二個字節(jié)的前面還是后面。這么說有點不太容易懂,讓我們來看一個例子:當你使用UTF-16存下某段內(nèi)容時,在不同的系統(tǒng)中它的后半部分可能是這樣的:
00 68?00 65?00 6C?00 6C?00 6F(高字節(jié)序,高位字節(jié)被存在前面)
68 00?65 00?6C 00?6C 00?6F 00(低字節(jié)序,低位字節(jié)被存在前面)
字節(jié)序方案只是一個微處理器架構(gòu)設(shè)計者的偏好問題,例如,Intel使用低字節(jié)序,Motorola使用高字節(jié)序。
舉個例子?!癆BC”這三個字符用各種方式編碼后的結(jié)果如下:
編碼類型碼值

UTF-32
UTF-32用四個字節(jié)表示代碼點,這樣就可以完全表示UCS-4的所有代碼點,而無需像UTF-8那樣使用復(fù)雜的算法。?與UTF-16類似,UTF-32也包括UTF-32、UTF-32BE、UTF-32LE三種編碼,UTF-32也同樣需要BOM字符。
文本編輯器怎么知道文本的編碼
當一個軟件打開一個文本時,它要做的第一件事是決定這個文本究竟是使用哪種字符集的哪種編碼保存的。軟件一般采用三種方式來決定文本的字符集和編碼:
檢測文件頭標識(BOM)
EF ? BB ? BF ? UTF-8 ? ???
FE ? FF ? UTF-16/UCS-2, ? big ? endian ? ???
FF ? FE ? UTF-16/UCS-2, ? little ? endian ? ???
FF ? FE ? 00 ? 00 ? UTF-32/UCS-4, ? little ? endian. ? ???
00 ? 00 ? FE ? FF ? UTF-32/UCS-4, ? big-endian.
軟件自己根據(jù)編碼規(guī)則猜測當前文件的編碼
提示用戶自己輸入當前文件的編碼
總結(jié):UTF-8、UTF-16、UTF-32只是一種編碼格式,不是字符集,是用來規(guī)范字符集存儲用的。其中UTF-8是變成的編碼規(guī)范,而UTF-16是固定用兩個字節(jié)表示一個碼值,UTF-32是固定使用四個字節(jié)表示一個碼值。
GB2312、GBK、GB18030之間的區(qū)別
GB2312編碼是第一個漢字編碼國家標準,是由中國國家標準總局1980年發(fā)布,1981年5月1日開始實施的一套國家標準,標準號是GB2312—1980。GB2312編碼適用于漢字處理、漢字通信等系統(tǒng)之間的信息交換,通行于中國大陸;新加坡等地也采用此編碼。中國大陸幾乎所有的中文系統(tǒng)和國際化的軟件都支持GB2312。GB2312編碼共收錄漢字6763個,其中一級漢字3755個,二級漢字3008個。同時,GB2312編碼收錄了包括拉丁字母、希臘字母、日文平假名及片假名字母、俄語西里爾字母在內(nèi)的682個全角字符。
GB2312是對ASCll碼的擴展,占用兩個字節(jié)。具體的編碼規(guī)則這邊就不介紹了,感興趣的讀者可以參考這篇博客。
GB2312能表示的漢字只有6000多個,但是中國的漢字有10萬之多,所以GB2312字符集還是不夠用,于是GBK出現(xiàn)了。GBK是對GB1212的擴展,也是占用2個字節(jié),GBK不再要求低字節(jié)一定是127號之后的內(nèi)碼,只要第一個字節(jié)是大于127就固定表示這是一個漢字的開始,不管后面跟的是不是擴展字符集里的內(nèi)容。GBK 包括了 GB2312 的所有內(nèi)容,同時又增加了近20000個新的漢字(包括繁體字)和符號。
GB18030采用變長編碼,可以是1個字節(jié)、2個字節(jié)和4個字節(jié)。是對GB2312和GBK的擴展,完全兼容兩者。
通過上面介紹,可以發(fā)現(xiàn)GBK、GB2312和GB18030字符集主要是對中文漢字的編碼,同時兼顧了一些其他常用符號的編碼。其中:
GB2312編碼方案出現(xiàn)最早,占用2個字節(jié),但是能表示的字符較少;
GBK也占用2個字節(jié),采用了不同的編碼方式,對GB2312進行了擴展,增加了近20000個新的漢字(包括繁體字)和符號;
GB18030采用變長編碼,可以是1個字節(jié)、2個字節(jié)和4個字節(jié)。是對GB2312和GBK的擴展,完全兼容兩者。
總結(jié):GB2312是我們中文自己的字符集,是國家標準。由于不能包含全部漢字,后面有擴展出現(xiàn)了GBK,GBK是固定長度編碼的字符集,后面又出現(xiàn)了變長編碼的GB18030字符集。