盡破字符亂碼

前言

使用新安裝的 myeclipse 打開了曾經的項目,發現原來的中文註釋,所有變成亂碼了。因而頭皮一陣發麻,對於喜歡透明的程序員來講,看不懂又想要弄明白的亂碼十分揪心。html

網上搜索了一陣,發現緣由是新安裝的 myeclipse 默認使用 cp1252 編碼,只要將其改爲支持中文字符的 UTF-8 便可解決中文亂碼問題。linux

  1. Preferences -> General -> Workspace -> Text file encoding
  2. 將 Text file encoding 改成 UTF-8

 

爲了完全弄明白字符亂碼的緣由,下功夫學習了一番,將學習和思考的內容寫在這篇博客中。nginx

字符編碼舉例

咱們在計算機屏幕上看到的是實體化的文字和符號,而在計算機存儲介質中存放的實際是二進制的比特流。把實體化的文字轉換成二進制比特流的過程,就是「編碼」,將比特流轉化成文字的過程,就是「解碼」。git

好比,「永」字在不一樣字符編碼表的十六進制和二進制編碼結果以下:程序員

字符編碼表 十六進制編碼 二進制編碼
UTF-8 E6B0B8C281 1110 0110 1011 0000 1011 1000 1100 0010 1000 0001
UTF-16 6C380081 0110 1100 0011 1000 0000 0000 1000 0001
GBK D3C03F 1101 0011 1100 0000 0011 1111

 

 

 

 

 

因而可知,若是在編碼和解碼是採用了不一樣的字符編碼表,那麼就會產生錯誤的解碼結果,有時甚至沒法看到結果。github

在文章開頭的例子中,就是使用了 UTF-8 的編碼方式,又採用 Cp1252 來解碼,致使了亂碼的結果。sql

字符編碼的概念

首先明確一些字符編碼的術語:編程

  • 字符庫(Character Repertoire):字符庫是字符抽象列表,具備超過一百萬中字符,包括拉丁字母、西裏爾字母、中文、韓文、日文、希伯來文、亞拉姆語等等,還包括其餘一些特殊符號。
  • 編碼字符集(Coded Character Set, CCS):編碼字符集是字符集和數字的映射。一個字符與一個數字惟一對應。
  • 字符編碼表(Character Encoding Form, CEF):字符編碼表是龐大的編碼字符集與較小的實際存儲的數值的轉換關係。
  • 編碼值(Code Point):編碼值就是惟一標識字符的數字。
  • 編碼單位(Code Unit):對於一個編碼值,能夠由多個編碼單位組成。這是由特定的字符編碼表決定的。

Unicode和UTF-8

 

Unicode 對世界上大部分的文字系統進行了整理和編碼,使得電腦能夠用更爲簡單的方式來呈現和處理文字。Unicode 使用使用16位的編碼空間,也就是每一個字符佔用2個字節。這樣理論上一共最多能夠表示2^16(即65536)個字符。eclipse

Unicode 的實現方式不一樣於編碼方式。一個字符的 Unicode 編碼是肯定的。可是在實際傳輸過程當中,因爲不一樣系統平臺的設計不必定相同,以及出於節省空間的目的,對 Unicode 編碼的實現方式有所不一樣。Unicode 的實現方式稱爲 Unicode 轉換格式(Unicode Transformation Format,簡稱爲 UTF)工具

例如,若是一個僅包含基本7位 ASCII 字符的 Unicode 文件,若是每一個字符都使用2字節的原 Unicode 編碼傳輸,其第一字節的8位始終爲0。這就形成了比較大的浪費。   因此,Unicode 就是上文提到的 CCS,諸如 UTF-8 就是一種 CEF。

下面講解 UTF-8 的編碼實現方式,能夠更好地理解字符編碼表,理解 UTF-8 與 Unicode 的轉換關係。

UTF-8編碼方式

UTF-8 (8-bit Unicode Transformation Format) 編碼爲變長編碼。編碼單位(Code Unit)爲一個字節。一個字節的前1-3個bit爲描述性部分,後面爲實際序號部分。

  • 若是一個字節的第一位爲 0,那麼表明當前字符爲單字節字符,佔用一個字節的空間。0 以後的全部部分(7個bit)表明在 Unicode 中的序號。
  • 若是一個字節以 110 開頭,那麼表明當前字符爲雙字節字符,佔用2個字節的空間。110 以後的全部部分(5個bit)加上後一個字節的除 10 外的部分(6個bit)表明在 Unicode 中的序號。且第二個字節以 10 開頭
  • 若是一個字節以 1110 開頭,那麼表明當前字符爲三字節字符,佔用2個字節的空間。1110 以後的全部部分(4個bit)加上後兩個字節的除 10 外的部分(12個bit)表明在 Unicode 中的序號。且第2、第三個字節以 10 開頭
  • 若是一個字節以 10 開頭,那麼表明當前字節爲多字節字符的第二個字節。10 以後的全部部分(6個bit)和以前的部分一同組成在 Unicode 中的序號。

具體每一個字節的特徵可見下表,其中 x 表明序號部分,把各個字節中的全部 x 部分拼接在一塊兒就組成了在Unicode字庫中的序號。

 

咱們分別看三個從一個字節到三個字節的 UTF-8 編碼例子:

 

細心的讀者能夠得出如下規律:

  • 3個字節的 UTF-8 十六進制編碼必定是以 E 開頭的
  • 2個字節的 UTF-8 十六進制編碼必定是以 C 或 D 開頭的
  • 1個字節的 UTF-8 十六進制編碼必定是以比 8 小的數字開頭的

還原myeclipse亂碼的過程,而且修正亂碼

如下代碼,使用 Navicat for MySQL 環境模擬。

那麼,文章最初提到的 myeclipse 打開文件亂碼,是怎麼產生的呢?如下還原了這個過程。

執行語句:

select hex( convert('這裏是註釋' using utf8) )

獲得結果:

E8BF99E9878CE698AFE6B3A8E9878A

執行語句:

select convert(0xE8BF99E9878CE698AFE6B3A8E9878A using latin1)

latin1 編碼就是 MySQL 中的 Cp1252 編碼

獲得結果:

这里是注释

上一行,就是咱們不肯看到可是出現了的亂碼!

 

假設咱們獲得了上面的亂碼結果,而後查看 myeclipse 的默認編碼,發現是 Cp1252,接着,咱們知道原文件是用 UTF-8 編碼的(或者說,猜想是 UTF-8 編碼),咱們就能夠進行亂碼的還原。

執行語句:

select hex( convert('这里是注释' using latin1) )

獲得結果:

E8BF99E9878CE698AFE6B3A8E9878A

執行語句:

select convert(0xE8BF99E9878CE698AFE6B3A8E9878A using utf8)

獲得結果:

這裏是註釋

 

到這裏,正確的文字信息顯示出來了!

注意:有些狀況下,使用錯誤的編碼打開文件,而後再次保存的時候,會出現提示,用當前編碼保存,將會丟失字符。這是由於新編碼沒法徹底解析舊編碼生成的二進制比特流。某些智能的 IDE 能夠提示用戶使用另一種編碼格式保存當前文件。

小結

本文簡單介紹了字符編碼的概念,闡述了亂碼產生的緣由。經過對 UTF-8 編碼方式的介紹,瞭解了一種經常使用的可變長度的字符編碼表。通常狀況下,亂碼會出如今編程開發的 IDE 和文本處理工具中。只要選擇了與文本被保存時相同的編碼,就能夠看到正確的文字。

另外,若是試圖識別亂碼,就須要知道顯示工具所使用的編碼和文件保存時使用的編碼。上文提到的 UTF-8 的編碼規律,能夠用來猜想文本在保存時是否可能使用了 UTF-8 編碼。經過解碼和再編碼的過程,能夠嘗試識別亂碼所要表達的文字。

附錄:經常使用的編碼及其含義

  • ascii – US ASCII
  • big5 – Big5 Traditional Chinese
  • gb2312 – GB2312 Simplified Chinese
  • gbk – GBK Simplified Chinese
  • latin1 – cp1252 West European
  • utf8 – UTF-8 Unicode

參考資料

  1. 十分鐘搞清字符集和字符編碼 (借鑑了不少)
  2. Wikipedia上的內容,包括「字符編碼」、「Unicode」、「UTF-8」、「ASCII」、「Windows-1252」等。
  3. 字符編碼筆記:ASCII,Unicode和UTF-8 (輔助參考)

 

創做日期:2016-03-05 星期六 8:49:14 PM

相關文章
相關標籤/搜索