from http://www.guokr.com/blog/763017/
http://blog.csdn.net/stilling2006/article/details/4129700
下載一個文件,一開啟發現是亂碼,不抓狂才怪…… 你們都知道,這都是字元編碼闖的禍。ASCII、ANSI、GB18030、Unicode、UTF-8、UTF-8 with BOM、UTF without BOM、UTF-16、UTF-16LE、UTF-16BE…… 一大坨的誰分得清?聽說UTF-8就是Unicode,但怎麼Windows記事本里的儲存選項有UTF-8和Unicode兩個選項呀?!究竟各種軟體是怎樣判斷一個檔案是什麼編碼呢?為什麼有時候又判斷錯誤呢?讓我一一道來。
世界上本沒有字元編碼。自從有了計算機,我們有了用0和1記錄文字的需求,於是字元就有了編碼。
== ASCII ==
ASCII編碼表示的“Hello GuoKr”(十進位制):72 101 108 111 32 71 117 111 75 114
ASCII是最基本的編碼,它定義了0~127對應的字元,包括最基本的英文字母、標點符號。它無法表示中文。ASCII編碼的文字,每一個位元組都是0~127,如果某個位元組大於127,那它一定不是ASCII編碼。
== GB* / ANSI ==
為了用計算機記錄並顯示中文,中國人發明了GB系列編碼。GB系列編碼定義了中文漢字、標點的編碼。按照GB系列編碼,在一段文字中,如果一個位元組是0~127,那麼這個位元組的含義同ASCII編碼,否則,這個位元組和下一個位元組共同組成漢字(或是GB編碼定義的其他字元)。因此,GB系列編碼向下相容ASCII,也就是說,如果一段用GB編碼文字里的所有字元都在ASCII中有定義,那麼這段編碼和ASCII編碼完全一樣。GB編碼早期收錄的漢字不足一萬個,基本滿足日常使用需求,但不包含一些生僻的字,後來在一個個新版本中加進去。最早的GB編碼是GB2312,後來有GBK,最新的是GB18030,加入了一些國內少數民族的文字,一些生僻字被編到4個位元組,每擴充套件一次都完全保留之前版本的編碼,所以每個新版本都向下相容。
同樣,日文、韓文、世界各國文字都有了它們各自的編碼(如果ASCII不能滿足使用要求的話)。這些編碼都和GB編碼相似,相容ASCII並用兩個位元組表示一個字。所有這些各國文字編碼,微軟統稱為ANSI 。所以即使知道是ANSI,我們還需要知道這是哪國文字才能解碼,因為這些編碼都互相沖突。另外,你無法用一段ANSI 編碼表示既有漢字、又有韓字的文字。
等等,上面我誤導大家了……其實是微軟誤導大家了,嚴格來說ANSI 不是字元編碼,而是美國一個非營利組織,他們做了很多標準制定工作,包括C語言規範ANSI C,還有各國文字編碼對應的“內碼表”(code page)。ANSI 規定簡體中文GB編碼的內碼表是936,所以GB編碼又叫做ANSI code page 936(按ANSI標準的內碼表936),各國編碼被統稱為ANSI 由來於此——這是個離譜的歷史錯誤,這就像我自己給國內的大學做了個排名,排名的依據是飯堂吃出蟲子的概率,然後國內的大學就被統稱為黃油貓。不過對於這些亂七八糟互相沖突的多國字元編碼,有個統稱還是不錯的(我了個去還要謝謝微軟),下面討論姑且繼續使用ANSI 。
==Unicode / UTF / UCS==
為了解決衝突的問題,促進世界和平,我們有了Unicode(聯合碼?)——一種將全世界所有語言所有字元都收錄在一起、讓它們和平共處的編碼……哦,等等,Unicode雖是一種字元編碼,但嚴格來說它和GB18030不能相提並論:它只定義了每一個字元對應一個整數(目前包含了十萬多個字元,其中0~127和ASCII完全一樣),但它沒有定義這個整數如何變成位元組。當你告訴我這段資料是Unicode編碼,啊,不好意思,我還是不知道該怎麼解碼——因為變成位元組流的格式不只一種,它們都叫做“Unicode轉換格式”(Unicode Transformation Format,簡稱為UTF)。
所以這裡面就有了一大堆UTF:UTF-8、UTF-8 with BOM、UTF-8 without BOM、UTF-16、UTF-16LE、UTF-16BE…… 還有很少見的UTF-32、,早期還會聽說過UCS-2、UCS-4…… _(:з」∠)_
等等,為什麼Windows記事本里有個儲存選項是Unicode?這個稍後說。
先說最常見的UTF-8:它將一個字元編為1-4個位元組,其中一個位元組的字元和ASCII 完全一致,所以它也向下相容ASCII。和ANSI類似,UTF-8第一個位元組決定了之後多少個位元組是一組好基友。多數漢字在UTF-8裡為3個位元組,有一些生僻的漢字會編到4位元組。
我們迎來第一種不相容ASCII的編碼:UTF-16。UTF-16以每2個位元組為一個單元,每個字元由1-2個單元組成,所以每個字元可能是2個位元組或者4個位元組,包括最常見的英文字母都會編成兩個位元組。大部分漢字也是2個位元組,少部分生僻字為4個位元組。UTF-16還有講究,一個單元中的兩個位元組的順序不是唯一的。學過計算機原理的同學知道,計算機中表示一個整數分兩種格式:低位在前高位在後,或者反過來。例如用兩個位元組表示260這個整數,可能是:
低位在前:04 01 (260=4+256*1)
高位在前:01 04 (260=256*1+4)
低位在前的UTF-16叫UTF-16LE,高位在前的叫UTF-16BE。目前絕大部分的計算機系統都使用低位在前的整數格式,所以如果沒有宣告,UTF-16預設是LE。
早期Unicode收編的字還不多時,兩個位元組足夠表示所有字元,所以有一種固定為兩個位元組的UTF,叫UCS-2。UTF-16的兩個位元組部分和UCS-2完全一樣,所以UTF-16向下相容UCS-2。UCS-2同樣分LE和BE。而Windows的記事本還有Windows其它地方所謂的Unicode,當代的Windows裡其實是UTF-16LE,在Windows XP和更早的版本里是UCS-2LE。微軟(又是微軟)正是混淆Unicode概念的禍首,微軟你這麼討厭你家人知道嗎?
此外,UTF-32和UCS-4固定為4個位元組一個字元,同樣分LE和BE。
還沒完,這麼多字元編碼,軟體開啟時如何知道是哪個編碼?於是不知道誰蹲坑時想出了BOM:在一個文字檔案或者一段字元編碼前加上幾個固定的位元組用於識別,這些位元組保證不對應任何一個字元,所以軟體一讀就能驗明正身:
EF BB BF - 我是UTF-8
FF FE - 我是UTF-16LE
FE FF - 我是UTF-16BE
(沒有BOM,直奔主題)-你猜?
不錯,沒BOM只能靠猜了。軟體讀入檔案時可以所有編碼都試一下,看哪個像。另外,BOM只針對Unicode系列編碼,ANSI通通不會有BOM。很顯然,沒有BOM難免偶然猜錯。網上就流傳了一個神奇的段子:開啟Windows記事本,打入“聯通”兩個字,儲存,關閉,再開啟,變成了個黑塊。記事本用ANSI(GB18030)儲存聯通這兩個字,剛好這兩個字的GB18030編碼看起來很像UTF-16,於是就當成UTF-16來開啟……
BOM聽起來很不錯,但實際是個討厭的設計,因為它和很多協議、規範不相容,這是題外話。
於是,UTF-8 with BOM、UTF-16 without BOM 你們就懂了。等等,如果不提BOM,究竟有BOM還是沒有BOM?—— 又是一個十分糾結的問題,Windows裡的軟體一般都預設有BOM,而其它系統都預設沒有BOM——可能是因為Windows常要相容ANSI的原因,特別依賴BOM來防止會錯意。
==誰是正統==
這麼多種編碼,用來寫文章就罷了,開啟個文件看到亂碼就退出、換個程式或者換個編碼再開啟;但寫程式時可半點馬虎不得,各種程式開發環境對編碼支援都不一樣,如果編碼沒搞好,你寫好的程式可能在別人計算機上就執行不起來了。如果我開發跨平臺的程式碼,而且要有中文(註釋),哪用什麼編碼好?以下是我所知道各開發環境/編譯器支援常見編碼情況(所有=ANSI、UTF-8 BOM and no BOM、UTF-16LE BOM and no BOM):
- Visual Studio:所有,儲存預設ANSI 。
- VC編譯器:所有,除了UTF-8 without BOM(直接當成是ANSI )
- Windows記事本:所有,儲存預設ANSI,無法儲存無BOM。
- XCode:只支援 UTF-8 without BOM。
- GCC:所有
- vim:所有,儲存預設為系統預設編碼,一般是UTF-8 without BOM。
- Eclipse:所有,儲存預設不明,Mac下居然是ANSI (我們中出了個叛徒)。
ANSI是無法跨境的,用GB寫的文件拿去韓國就果斷亂碼了。光是簡體中文系統,ANSI 也是經常被認錯的,Eclipse裡經常(不總是)出現開啟ANSI 檔案是亂碼的情況,這是因為ANSI 沒有很明顯的特徵。XCode和Mac的文字編輯器開啟ANSI 直接是亂碼,因為明確不支援。ANSI 容錯性普遍比較差,一個位元組錯了可能導致後面的字通通掛掉。為了防止Eclipse等發神經,也為免跨國帶來麻煩,更為了你自己的資料安全,請遠離ANSI。
UTF-16不相容ASCII,不相容C語言的字串處理庫函式(因為位元組流裡有\0),除了Windows愛用,其它系統都痛恨它。BOM和很多協議規範衝突,很多軟體都抵制,也是隻有Windows常用,而且將其列為正統(作反)。
綜上,跨平臺開發請使用UTF-8 without BOM,那是最通用的編碼,是很多軟體系統的預設編碼,你在看的網頁也用它。它特徵明顯,除了VC編譯器和微軟的各種軟體外暫時沒發現哪個軟體會有認錯的情況。它還有經過精心設計的容錯機制,錯一個位元組最多隻會錯一個字元。請手動設定你的開發環境,將預設儲存的編碼設為UTF-8 without BOM,並將其它編碼的檔案轉換過來,亂碼就拜拜啦;記事本等不支援的編輯器,不要讓他們摸你的檔案。至於VC編譯器硬要鬧彆扭就由它去吧 _(:з」∠)_
(經過測試,VS2010能正常開啟UTF-8 without BOM的程式碼,但可能編譯不通過,並報奇怪的編譯錯誤)
為什麼程式猿那麼喜歡黑微軟啊?因為微軟那群悶騷的設計師總是和大家不太一樣……
寫在最後:
在這個機器能聽懂人語的年代,我們還要操心字元編碼這麼低階的事,真是不可思議…… 解決方案這麼簡單:幾家大公司坐一圈,說定一種編碼,以後通通用一種編碼。而且這種編碼已經浮出水面、支援成熟:UTF-8。我很讚賞蘋果,他們果斷和ANSI還有一大堆不統一的編碼一刀兩段,所有軟體只支援UTF-8,這才是真正對使用者負責 —— 而微軟到今天還死抱著ANSI不放,在這件小事上就看出他就是如此不捨得過去一點的成就,如此不捨得革自己的命。