在日常開發工作中,我們常常會使用到XML,早已成為了一種標准。它的用途很的廣泛,但這些不是本文所重點討論的。
相信大家在做開始時候常常碰到過“亂碼”的問題,這是中國程序猿非常頭疼的問題。我一直非常想深入研究關於“編碼”的原理,無奈水平有限,那些枯燥的 理論(二進制,ASCII,Unicode,UTF-8,gb2312,ISO ...光這些就讓我看的兩眼發黑了),實在看不下去。也非常難真正搞懂搞明確。望各位網友多指點......
我將用工作中遇到的一個“XML文件亂碼”的簡單問題,解決這個問題,分析其背后的原理。
首先,我們在本地新建一個文本文件。將后綴名改為".XML”, 然后用用記事本打開,往里面加入一些符合XML文檔規范的內容。
如圖所看到的:
寫好之后,按“ctrl+s”保存,然后使用IE瀏覽器打開該XML文件,驗證該XML文檔的規范及正確性。不料。竟然解析出錯了,例如以下:
這是咋回事呢?我的XML文檔定義的格式好像沒問題啊。無效字符?這肯定是典型的“編碼”問題了。聰明的我第一就想到了,調整IE瀏覽器的“編碼”嘛。
但是打開“查看”“編碼”。發現那些編碼格式全是灰色的,好像不能選擇哦。
這是由於,在定義XML文檔的時候,指定了編碼格式為"UTF-8",這就相當於告訴了瀏覽器(XML解析引擎):你必須使用"UTF-8"編碼去解析我,所以無法使用其它的編碼格式去查看了。
這是由於,我們在使用記事本保存該文檔的時候,沒有選擇編碼格式。默認使用的是操作系統編碼(中文版的系統)。也就是相應的"GB2312”編碼。 當我們的IE瀏覽器,再使用我們指定的UTF-8編碼去解析該XML文檔的時候。出現了亂碼,所以造成了上面的錯誤。(Windows中的文件保存在硬盤 上,默認使用操作系統編碼。
比方我們XML文檔中定義的“中國”這兩個字,保存好后,假如其相應的GB2312可能是"10001"。而在UTF-8編碼 中的,“10001”相應的就不是“中國”了,要么找不到,要么是亂碼。所以IE就拒絕顯示了)。那我們應該怎么辦呢?有兩種辦法能夠解決。
第一,我們在xml文檔定義時。指定其編碼為gb2312,例如以下圖所看到的:
保存之后,我們再使用IE瀏覽器打開,結果如圖:
恭喜,這個問題攻克了。
可是這樣的方法不推薦使用。
由於我們在定義XML文檔時候,為了文檔的通用性。我們一般使用UTF-8編碼。
另外一種方法:
我們再用記事本打開該文檔。點擊“另存為”,發現以下會有“編碼”選項,選擇“UTF-8”之后再試。
事實上。我們在使用諸如 Eclipse 或者Microsoft Visual Studio之類的開發工具來定義XML文檔,並不會碰到上面的問題。原因是這些IDE都非常“聰明”,你的XML文檔指定的是那種編碼格式。IDE在將 XML文檔保存到硬盤的時候,就自己主動使用那種格式。所以,非常多局限於使用某種IDE開發的程序猿,事實上並不明確這些知識及其背后的原理,但他們做開發起來 一樣非常順手。早年據筆者了解,國內有非常多大牛。寫代碼都是用EditPlus之類的文本編輯器。而那些在Linux/unix上面的大牛。非常多都是用VI /VIM來編碼。大概這就是差距吧。(呵呵。當然這不是本文討論的重點)
補充一點理論知識,不暈的就繼續讀下去吧。
在最初的時候,Internet上僅僅有一種字符集——ANSI的ASCII字符集,它使用7 bits來表示一個字符,總共表示128個字符。當中包含了英文字母、數字、標點符號等經常使用字符。之后。又進行擴展,使用8 bits表示一個字符,能夠表示256個字符。主要在原來的7 bits字符集的基礎上增加了一些特殊符號比如制表符。
后來,因為各國語言的增加,ASCII已經不能滿足信息交流的須要,因此,為了可以表示其他國家的文字。各國在ASCII的基礎上制定了自己的字符 集,這些從ANSI標准派生的字符集被習慣的統稱為ANSI字符集。它們正式的名稱應該是MBCS(Multi-Byte Chactacter System,即多字節字符系統)。這些派生字符集的特點是以ASCII 127 bits為基礎,兼容ASCII 127,他們使用大於128的編碼作為一個Leading Byte。緊跟在Leading Byte后的第二(甚至第三)個字符與Leading Byte一起作為實際的編碼。這種字符集有非常多。我們常見的GB-2312就是當中之中的一個。
標准的 Unicode稱為UTF-16。
后來為了雙字節的Unicode可以在現存的處理單字節的系統上正確傳輸,出現了UTF-8,使用類似MBCS的方式對 Unicode進行編碼。
注意UTF-8是編碼,它屬於Unicode字符集。
Unicode字符集有多種編碼形式,而ASCII僅僅有一種,大多數 MBCS(包含GB-2312)也僅僅有一種。
而其UTF-8編碼為:E8 BF 9E E9 80 9A
開頭字節 Charset/encoding
EF BB BF UTF-8
FE FF UTF-16/UCS-2, little endian
FF FE UTF-16/UCS-2, big endian
FF FE 00 00 UTF-32/UCS-4, little endian.
00 00 FE FF UTF-32/UCS-4, big-endian.
比如插入標記后,連通”兩個字的UTF-16 (big endian)和UTF-8碼分別為:
FF FE DE 8F 1A 90
EF BB BF E8 BF 9E E9 80 9A
可是MBCS文本沒有這些位於開頭的字符集標記,更不幸的是,一些早期的和一些設計不良的軟件在保存Unicode文本時不插入這些位於開頭的 字符集標記。
因此。軟件不能依賴於這樣的途徑。
這時,軟件能夠採取一種比較安全的方式來決定字符集及其編碼,那就是彈出一個對話框來請示用戶。比如將那個 “連通”文件拖到MS Word中。Word就會彈出一個對話框。
假設軟件不想麻煩用戶。或者它不方便向用戶請示。那它僅僅能採取自己“猜”的方法,軟件能夠依據整個文本的特征來推測它可能屬於哪個charset,這就非常可能不准了。使用記事本打開那個“連通”文件就屬於這樣的情況。
我們能夠證明這一點:在記事本中鍵入“連通”后,選擇“Save As”,會看到最后一個下拉框中顯示有“ANSI”。這時保存。當再當打開“連通”文件出現亂碼后,再點擊“File”->“Save As”,會看到最后一個下拉框中顯示有“UTF-8”,這說明記事本覺得當前打開的這個文本是一個UTF-8編碼的文本。
而我們剛才保存時是用ANSI字 符集保存的。這說明,記事本推測了“連通”文件的字符集。覺得它更像一個UTF-8編碼文本。這是由於“連通”兩個字的GB-2312編碼看起來更像 UTF-8編碼導致的。這是一個巧合,不是全部文字都這樣。能夠使用記事本的打開功能,在打開“連通”文件時在最后一個下拉框中選擇ANSI,就能正常顯 示了。反過來,假設之前保存時保存為UTF-8編碼,則直接打開也不會出現故障。
假設將“連通”文件放入MS Word中,Word也會覺得它是一個UTF-8編碼的文件,但它不能確定,因此會彈出一個對話框詢問用戶,這時選擇“中文簡體(GB2312)”,就能正常打開了。
記事本在這一點上做得比較簡化罷了。這與這個程序的定位是一致的。