字符集與字符編碼 (charset & encoding)

亂碼是個大坑,相信每一個人都遇過,並且是個繞不過去的坑。我理解每一個程序員都應該寫一篇編碼相關的博文,梳理本身對這一塊的理解,下面是我反覆理解屢次以後的學習小結。html

 

一、從記事本的不一樣編碼提及:程序員

打開記事本,輸入「我我」,保存爲ansi編碼(實際上是gb2312,這也是默認編碼)。再分別另存爲unicode(實際上是utf-16 little endian)、unicodeBigEndian(實際上是utf-16 big endian)、utf8,用UltraEdit打開,切換到二進制模式,內容以下:windows

編碼 內容
ansi CE D2 | CE D2
unicode FF FE | 11 62 | 11 62
unicode big endian FE FF | 62 11 | 62 11
utf-8 EF BB BF | E6 88 91 | E6 88 91

能夠看出,「我」在gb2312裏的編碼是CE D2,在utf-16 little endian裏是11 62,在utf-8裏是E6 88 91。FF FE是文件頭,用來標識這個文件是unicode little endian格式的,同理,EF BB BF標識文件是utf-8格式。所謂的endian,是字節順序,不一樣的操做系統,可能小字節在前、也可能大字節在前,既然不同,就用一個文件頭(學名叫BOM,byte order mark,字節順序標識符)來標識。具體方法是:找一個在unicode裏不存在的字符(FF FE)來表示,EF BB BF是FF FE在utf-8裏的編碼。各類編碼的BOM詳見下表:數組

bytes encoding
EF BB BF utf-8
FF FE utf-16 little endian
FE FF utf-16 big endian
FF FE 00 00 utf-32 little endian
00 00 FE FF utf-32 big endian

 

二、亂碼的各類現象工具

文本保存在文件裏都是字節byte,那這些byte數組是怎麼顯示爲具體的字符呢?以ansi格式的「我」爲例,CE D2顯示在界面上的步驟是這樣的:windows首先將CE D2轉換成它內部使用的編碼格式unicode,而後按照unicode編碼去字體文件中查找字體圖像,最後將圖像畫在窗口的指定位置上。以下:學習

  1. 字符首先以某種編碼保存在文件中。
  2. windows將文件中的編碼映射爲unicode編碼。
  3. windows根據unicode編碼去字體文件中查找字體圖像,並畫在窗口上。

這3步中的每一步錯了,都體現爲一種典型的亂碼。字體

  1. 錯誤1:若是弄錯了編碼格式,好比將「我」的utf-16編碼11 62錯存成了gb2312的文件,就會出現亂碼。
  2. 錯誤2:若是映射到unicode出錯,例如出現了unicode裏未定義的字符編碼,windows就會使用缺省字符,一般是?。好比「我」的gb2312編碼CE D2在unicode裏還沒有使用,因此映射不到任何一個有效的unicode編碼。
  3. 錯誤3:若是映射到了unicode編碼,但在字體文件中找不到對應的字符,windows就會顯示字體文件中的缺省圖像:空白或方格。

 

三、各類編碼ascii、ansi(gb2312/big5/...)、unicode(utf8/16/32/...)編碼

1) ascii用1個字節(共255個)表示全部英文字符。缺點很明顯,就是不夠用。對於歐洲那些表音的字母類,就已經捉襟見肘了,再遇到中日韓的表義字符動輒上萬,就更不夠了。因而ansi想出來填坑,但結果是反而把事情搞麻煩了。其實若是一開始就想到字符不夠用的問題,再直接整出個全球統一字符集unicode,就不會有這個大坑了。因此說白了,亂碼問題是個歷史緣由形成的問題(話說軟件裏那麼大坑,哪些不是歷史緣由形成的!Y2K也是一例,可是誰也沒那個遠見,因此只能迂迴前進了)。url

 

2) 而後,各國都發現ascii不夠用,因而各自造出本身的編碼來知足須要,中國造出gb23十二、臺灣造出big5大五碼、日本造出shift_jis、其餘阿拉伯國家、印度也是相似的。先是IBM弄了代碼頁(CodePage)、再是微軟繼承了這一套再加了些本身的定義,整成了ansi,包括全部地區的代碼頁。經過這些代碼頁,windows能夠實現各地區編碼與unicode之間的相互轉換。(注:代碼頁的具體文件爲:c:\windows\system32\*.nls文件)代碼頁是爲了兼容已有的程序而存在的,若是咱們能強制全部程序都轉到unicode,那各地區的ansi編碼也就沒有存在的必要了,說白了只是發展過程當中的中間產物。spa

 

3) 最後是unicode。

3.1) 先說說字符集與字體編碼的區別。對大多數字符集來講,字符集裏的編碼 = 字符編碼。好比「我」字,在gb2312中的編碼是CE D2,在big5中是A7 DA。惟獨在Unicode裏例外,Unicode使用4個字節/32位(實際只用了31位,最高位必須爲0)惟一表示全世界全部可能的字符。unicode的容量爲231-1 ≈ 21億個,截止到2005年只用了10萬個,因此應該足夠用了。但每一個字符4字節,太浪費空間了,因而就產生了各類編碼方式,常見的有utf-八、utf-1六、utf-32等。這裏,unicode是字符集,utf-8/16/32是字符編碼。一樣是「我」字,在unicode裏是00 00 62 11,在utf-8裏是E6 88 91,在utf-16裏是62 11,在utf-32裏纔是00 00 62 11。

3.2) unicode到utf8/16/32的轉換方式就不詳述了。無非又是一些位操做,對理解這個問題的主幹無益。有興趣的能夠參看本文最後的引用。

 

四、對比一下各編碼的存儲效率

  1. 存儲英文時:utf-8 = ansi > utf-16 > utf-32
  2. 存儲中文時:ansi ≈ utf-16 > utf-8 > utf-32

固然,這裏說的是大多數的狀況,中英文混雜、多語言混雜、小語種文本都須要具體討論。

 

五、參考

  1. 伐木丁丁鳥鳴嚶嚶:《淺談文字編碼和Unicode(上)》和他作的小工具CodeView:快速查看文本編碼
  2. 阮一峯:《字符編碼筆記:ASCII,Unicode和UTF-8
  3. 中韓翻譯網 金聖鎮:《字體編輯用中日韓漢字Unicode編碼表
相關文章
相關標籤/搜索