字符編碼筆記:ASCII、ANSI、Unicode

一、基礎(chǔ)知識

計算機(jī)中儲存的信息都是用二進(jìn)制數(shù)表示的;而我們在屏幕上看到的英文、漢字等字符是二進(jìn)制數(shù)轉(zhuǎn)換之后的結(jié)果。通俗的說,按照何種規(guī)則將字符存儲在計算機(jī)中,如'a'用什么表示,稱為"編碼";反之,將存儲在計算機(jī)中的二進(jìn)制數(shù)解析顯示出來,稱為"解碼",如同密碼學(xué)中的加密和解密。在解碼過程中,如果使用了錯誤的解碼規(guī)則,則導(dǎo)致'a'解析成'b'或者亂碼。

  • 字符集(Charset):是一個系統(tǒng)支持的所有抽象字符的集合。字符是各種文字和符號的總稱,包括各國家文字、標(biāo)點符號、圖形符號、數(shù)字等;
  • 字符編碼:字符編碼就是以二進(jìn)制的數(shù)字來對應(yīng)字符集的字符。絕對字符編碼是信息技術(shù)交流的基礎(chǔ);
  • 內(nèi)碼:在計算機(jī)科學(xué)及相關(guān)領(lǐng)域當(dāng)中,內(nèi)碼指的是“將資訊編碼后,透過某種方式儲存在特定記憶裝置時,裝置內(nèi)部的編碼形式”。在不同的系統(tǒng)中,會有不同的內(nèi)碼,在以往的英文系統(tǒng)中,內(nèi)碼為ASCII。 在繁體中文系統(tǒng)中,目前常用的內(nèi)碼為大五碼Big5。在簡體中文系統(tǒng)中,內(nèi)碼則為國標(biāo)碼GB18030。為了軟件開發(fā)方便,如國際化與本地化,現(xiàn)在許多系統(tǒng)會使用Unicode作為內(nèi)碼,常見的操作系統(tǒng)Windows、Mac OS X、Liinux皆如此。許多編程語言也采用Unicode為內(nèi)碼,如Java、Python 3。

二、字符編碼分類

常見字符集名稱:ASCII字符集、GB2312字符集、BIG5字符集、GB18030字符集、Unicode字符集等。計算機(jī)要準(zhǔn)確的處理各種字符集文字,需要進(jìn)行字符編碼,以便計算機(jī)能夠識別和存儲各種文字。

2.1ASCII字符集&編碼

ASCII(American Standard Code for Information Interchange,美國信息交換標(biāo)準(zhǔn)代碼)是基于拉丁字母的一套電腦編碼系統(tǒng)。它主要用于顯示現(xiàn)代英語,而其擴(kuò)展版本EASCII則可以勉強(qiáng)顯示其他西歐語言。它是現(xiàn)今最通用的單字節(jié)編碼系統(tǒng)(但是有被Unicode追上的跡象),并等同于國際標(biāo)準(zhǔn)ISO/IEC 646。

  • ASCII字符集:主要包括控制字符(回車鍵、退格、換行鍵等);可顯示字符(英文大小寫字符、阿拉伯?dāng)?shù)字和西文符號)。
  • ASCII編碼:將ASCII字符集轉(zhuǎn)換為計算機(jī)可以接受的數(shù)字系統(tǒng)的數(shù)的規(guī)則。使用7位(bits)表示一個字符,共128字符;但是7位編碼的字符集只能支持128個字符,為了表示更多的歐洲常用字符對ASCII進(jìn)行了擴(kuò)展,ASCII擴(kuò)展字符集使用8位(bits)表示一個字符,共256字符。ASCII字符集映射到數(shù)字編碼規(guī)則如下圖所示:
    image.png

ASCII只能支持現(xiàn)代美國英語,對更多其他語言語言的顯示則無能為力。

2.2 ANSI多字節(jié)編碼(本地化)

對于中文 DOS 系統(tǒng)和早期的中文 Windows 系統(tǒng),大陸制定了國標(biāo)碼 GB2312,臺港澳地區(qū)則使用了大五碼 Big5。微軟針對這些本地化字符編碼采用的就是用 ANSI(American National Standards Institute,美國國家標(biāo)準(zhǔn)學(xué)會)多字節(jié)編碼方式,系統(tǒng)里的英文和符號就使用單字節(jié)的 ASCII(0x00~0x7f),而對于漢字之類的本地化字符編碼,就采用 0x80~0xFF 范圍內(nèi)的多個字節(jié)來表示,這樣既能兼容 ASCII ,又能正常使用本地化語言文字。大陸的國標(biāo)碼發(fā)展了好幾代,歸結(jié)如下:

  • GB2312:1980年發(fā)布,收錄了7445個字符,包括6763個漢字和682個其它符號。漢字是雙字節(jié)編碼。
  • GBK:1995年發(fā)布,收錄了21886個符號,包括21003個漢字和883個其它符號。漢字是雙字節(jié)編碼。簡體中文 Windows 目前默認(rèn)采用這種本地化編碼。
  • GB18030-2000:2000年發(fā)布,收錄了27533個漢字,漢字分為雙字節(jié)編碼部分和四字節(jié)編碼部分。
  • GB18030-2005:2005年發(fā)布,收錄了70244個漢字,漢字也分為雙字節(jié)編碼部分和四字節(jié)編碼部分。

ANSI 多字節(jié)編碼解決了各種語言文字的本地化使用問題,也有它自己的缺陷:各地制定的編碼標(biāo)準(zhǔn)只對自己的語言文字有效,而不同語言文字的編碼都是沖突的,因為大家都用 0x80~0xFF 范圍字節(jié)表示自己的語言文字,而不考慮別的語言文字如何編碼,沖突在所難免。比如簡體中文(GBK)的文本放到繁體中文(Big5)的操作系統(tǒng)里,就被默認(rèn)解析成繁體字編 碼,兩種編碼是沖突的,就會顯示混亂的繁體字,反過來也一樣。微軟公司使用了代碼頁(Codepage)轉(zhuǎn)換表的技術(shù)來過渡性的部分解決這一問題,即通過指定的轉(zhuǎn)換表將非 Unicode 的字符編碼轉(zhuǎn)換為同一字符對應(yīng)的系統(tǒng)內(nèi)部使用的 Unicode 編碼。可以在“語言與區(qū)域設(shè)置”中選擇一個代碼頁作為非 Unicode 編碼所采用的默認(rèn)編碼方式,如936為簡體中文GBK,950為正體中文Big5(皆指PC上使用的)。在這種情況下,一些非英語的歐洲語言編寫的軟件和文檔很可能出現(xiàn)亂碼。而將代碼頁設(shè)置為相應(yīng)語言中文處理又會出現(xiàn)問題,這一情況無法避免。從根本上說,完全采用統(tǒng)一編碼才是解決之道,但目前尚無法做到這一點。

2.3 Unicode問題(國際化)

Unicode編碼也叫萬國碼、國際碼等,Unicode字符集可以簡寫為UCS(Unicode Character Set)。早期的Unicode標(biāo)準(zhǔn)有UCS-2、UCS-4的說法。UCS-2用兩個字節(jié)編碼,UCS-4用4個字節(jié)編碼。這個僅僅是標(biāo)準(zhǔn),而不是實現(xiàn),在編碼實現(xiàn)的過程中,有些考慮兼容舊的單字節(jié) ASCII 編碼,有些不考慮兼容性;有些考慮雙字節(jié)中哪個字節(jié)放在前面,哪個字節(jié)放在后面的問題,即 BOM(Byte Order Mark,字節(jié)順序標(biāo)記)的作用。因此誕生了多種國際碼的實現(xiàn)方式,統(tǒng)稱為 Unicode 轉(zhuǎn)換格式(Unicode Transformation Format,UTF):

  • UTF-8:靈活的變長編碼,對于 ASCII 使用一個字節(jié)編碼,其他本地化語言文字用多個字節(jié)編碼,最長可以到 6 個字節(jié)編碼一個字符。對于漢字,通常是 3 個字節(jié)表示一個漢字。這是 Unix/Linux 系統(tǒng)默認(rèn)的字符編碼。
  • UTF-16:兼容 UCS-2,一般都是兩字節(jié)表示一個字符,對于超出兩字節(jié)的國際碼字符,使用一對兩字節(jié)來表示。在存儲時,按兩個字節(jié)的排布順 序,可以分為 UTF-16LE(Little Endian,小端字節(jié)序)和UTF-16BE(Big Endian,大端字節(jié)序),微軟所說的 Unicode 默認(rèn)就是 UTF-16LE。
  • UTF-32:同 UCS-4,因為用四個字節(jié)表示一個字符,所以不需要考慮擴(kuò)展了。這種編碼方式簡單,但也特別浪費空間,所以應(yīng)用很少。在存儲時也分為 UTF-32BE 和 UTF-32LE,因為用得少,所以不用太關(guān)心這種編碼格式。

三、Little endian和Big endian

UCS-2 格式可以存儲 Unicode 碼(碼點不超過0xFFFF)。以漢字嚴(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ī)范中推薦的標(biāo)記字節(jié)順序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。Unicode 規(guī)范定義,每一個文件的最前面分別加入一個表示編碼順序的字符,這個字符的名字叫做"零寬度非換行空格"(zero width no-break space),用FEFF表示。這正好是兩個字節(jié),而且FF比FE大1。

如果一個文本文件的頭兩個字節(jié)是FE FF,就表示該文件采用大頭方式;如果頭兩個字節(jié)是FF FE,就表示該文件采用小頭方式。

UTF-8不需要BOM來表明字節(jié)順序,但可以用BOM來表明編碼方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8編碼是EF BB BF。所以如果接收者收到以EF BB BF開頭的字節(jié)流,就知道這是UTF-8編碼了。


四、BOM

  1. BOM的來歷
    為了識別 Unicode 文件,Microsoft 建議所有的 Unicode 文件應(yīng)該以 ZERO WIDTH NOBREAK SPACE(U+FEFF)字符開頭。這作為一個“特征符”或“字節(jié)順序標(biāo)記(byte-order mark,BOM)”來識別文件中使用的編碼和字節(jié)順序。
  2. 不同系統(tǒng)對BOM的支持
    因為一些系統(tǒng)或程序不支持BOM,因此帶有BOM的Unicode文件有時會帶來一些問題。

①JDK1.5以及之前的Reader都不能處理帶有BOM的UTF-8編碼的文件,解析這種格式的xml文件時,會拋出異常:Content is not allowed in prolog。

②Linux/UNIX 并沒有使用 BOM,因為它會破壞現(xiàn)有的 ASCII 文件的語法約定。

③不同的編輯工具對BOM的處理也各不相同。使用Windows自帶的記事本將文件保存為UTF-8編碼的時候,記事本會自動在文件開頭插入BOM(雖然BOM對UTF-8來說并不是必須的)。而其它很多編輯器用不用BOM是可以選擇的。UTF-8、UTF-16都是如此。

  1. 決定文本的字符集與編碼
    對于Unicode文本最標(biāo)準(zhǔn)的途徑是檢測文本最開頭的幾個字節(jié)。如:

開頭字節(jié) Charset/encoding
EF BB BF    UTF-8
FE FF     UTF-16/UCS-2, little endian(UTF-16LE)
FF FE     UTF-16/UCS-2, big endian(UTF-16BE)
FF FE 00 00  UTF-32/UCS-4, little endian.
00 00 FE FF  UTF-32/UCS-4, big-endia

參考文獻(xiàn)

  1. 《字符,字節(jié)和編碼》http://www.regexlab.com/zh/encoding.htm
  2. 《字符編碼詳解——徹底理解掌握編碼知識,“亂碼”不復(fù)存在》http://blog.51cto.com/polaris/377468
  3. 《字符編碼筆記:ASCII,Unicode 和 UTF-8》http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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