從XML文件亂碼問題,探尋其背后的原理


在日常開發工作中,我們常常會使用到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就是當中之中的一個。
  比如在GB-2312字符集中,“連通”的編碼為C1 AC CD A8,當中C1和CD就是Leading Byte。前127個編碼為標准ASCII保留,比如“0”的編碼是30H(30H表示十六進制的30)。軟件在讀取時。假設看到30H,知道它小於 128就是標准ASCII,表示“0”。看到C1大於128就知道它后面有一個另外的編碼。因此C1 AC一同構成一個整個的編碼,在GB-2312字符集中表示“連”。
  因為每種語言都制定了自己的字符集。導致最后存在的各種字符集實在太多,在國際交流中要經常轉換字符集很不便。因此,提出了 Unicode字符集,它固定使用16 bits(兩個字節、一個字)來表示一個字符,共可以表示65536個字符。將世界上差點兒全部語言的經常使用字符收錄當中,方便了信息交流。

標准的 Unicode稱為UTF-16。

后來為了雙字節的Unicode可以在現存的處理單字節的系統上正確傳輸,出現了UTF-8,使用類似MBCS的方式對 Unicode進行編碼。

注意UTF-8是編碼,它屬於Unicode字符集。

Unicode字符集有多種編碼形式,而ASCII僅僅有一種,大多數 MBCS(包含GB-2312)也僅僅有一種。

  比如“連通”兩個字的Unicode標准編碼UTF-16 (big endian)為:DE 8F 1A 90
而其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)”,就能正常打開了。

記事本在這一點上做得比較簡化罷了。這與這個程序的定位是一致的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM