1. 程式人生 > >字元編碼:ASCII、Unicode、UTF-8

字元編碼:ASCII、Unicode、UTF-8

  網際網路時代,知乎大V通過段子抖機靈,公眾號大V通過雞湯獲得關注,微博大V通過新聞搶眼球,我們作為普通看客則刷的不亦樂乎。但是這些文章都有一個不引人注意的共同點,那就是它們都是由字元組成的(好吧,果然說的是廢話☺)。字元君,也就是今天要講的主角。

  這一篇篇排版精美、引人入勝的文章都是由一個個字元組成的,集字成句,集句成段,集段成文,最終呈現在我們面前。然而這些字元在計算機內部是怎樣表示的,又是怎樣顯示在螢幕上的卻沒有多少人瞭解。其實計算機經過了這麼多年的發展,字元編碼都已經基本形成了國際通用的標準,各種IDE、瀏覽器、文書處理軟體已經都幫我們處理好了,我們不再需要在字元編碼方面下很多功夫了。但是作為一個程式猿我們必須得懂得一點字元編碼的知識,這不僅是程式猿的專業素質,也是因為假如需要做應用國際化(Internationalization)的時候不會出現各種亂碼了(%¥&*#@¥%……#%),話說在網上下載字幕匯入視訊中的時候偶爾也會出現亂碼,需要我們用Nodepad++選擇合適的編碼格式才能正確的觀看。

ASCII編碼

  ASCII(American Standard Code for Information Interchange,美國資訊交換標準程式碼),它主要用於顯示現代英語和其他西歐語言。ASCII至今為止共定義了128個字元,其中包括95個可顯示字元和33個無法顯示的控制字元(多數都已經廢除)。
  在計算機的最底層,所有的資訊都是用二進位制的串來表示的,每一個二進位制位(bit)都有0和1兩種狀態,所以8個二進位制位(一個位元組byte)可以組合出256種狀態。ASCII的128個字元,只佔用了一個位元組的後面7位,最高位(b7)一般用作奇偶校驗位

缺點:

  ASCII的侷限在於只能顯示26個基本拉丁字母、阿拉伯數目字和英式標點符號,因此只能用於顯示現代美國英語。

  隨著計算機在全世界的流行,使用計算機的人越來越多,不再侷限於使用英語的地區,這時候ASCII編碼的問題就出現了–只佔用1個位元組的ASCII編碼最多隻能表示256個字元,那要怎麼才能表示其他的語言呢。要知道,僅僅只是《漢字大字典》中收錄的漢字就有6萬多個,更何況還有很多小地區都有著很多不同的文字。如果任由各個語言的人自行訂立編碼規則的話,那就會造成語言的壁壘了,可能出現廣東的人收到北京的人的郵件一開啟全是亂碼的情況,因為廣東編碼並不相容北京編碼。-_-#
  所以需要出現一種很大容量的編碼,將世界上所有的文字字元都囊括在內,每一個字元都能在其中找到獨一無二的存在。這個時候,Unicode編碼粉墨登場了。

Unicode編碼

  Unicode(萬國碼、國際碼、統一碼)是電腦科學領域裡的一項業界標準。它對世界上大部分的文字系統進行了整理、編碼,使得電腦可以用更為簡單的方式來呈現和處理文字。Unicode是一個很大的集合,現在的規模可以容納100多萬個字元,已經收入超過十萬個字元,Unicode 是一個很大的集合,現在的規模可以容納100多萬個符號,每個符號的對應的二進位制都不一樣。Unicode 規定可以使用多個位元組表示一個字元,例如 a 的編碼為 01100001,一個位元組就夠了,漢字“好”的編碼為 01011001 01111101,則需要兩個位元組。

為了相容ASCII,Unicode 規定前0~127個字元與ASCII是一樣的,不一樣的只是128~255的這一段。完整的Unicode編碼請檢視:unicode.org

問題:

  但是Unicode只是一個符號集,它只規定了符號的二進位制程式碼,卻沒有規定這個二進位制程式碼應該如何儲存,也就是並沒有說明在計算機內部實現中二進位制程式碼到底是長得什麼樣子。
  比如,漢字”強”的Unicode是十六進位制數U+5F3A,轉換成二進位制數為0101 1111 0011 1010,這個字元的表示至少需要2個位元組。表示其他更大的符號,可能需要3個位元組或者4個位元組,甚至更多。
  這裡就有兩個嚴重的問題:
  1.如何才能區別Unicode和ASCII或者其他編碼呢?計算機怎麼知道三個位元組表示一個Unicode符號,還是分別表示的三個ASCII符號呢?
  2.如果Unicode統一規定,每個符號用四個位元組的定長表示,而英文字母只用一個位元組表示就夠了,那麼每個英文字母前都必然有二到三個位元組是0,這對於儲存來說是極大的浪費,文字檔案的大小會因此大出二三倍,這是無法接受的。

  UTF-8就是在網際網路上使用最廣的一種Unicode的實現方式。其他實現方式還包括UTF-16(字元用兩個位元組或四個位元組表示)和UTF-32(字元用四個位元組表示),不過在網際網路上基本不用。所以兩者之間的關係是:UTF-8是Unicode的在計算機內部的實現方式之一

UTF-8

  UTF-8(8-bit Unicode Transformation Format)是一種針對Unicode的可變長度字元編碼。它可以用來表示Unicode標準中的任何字元,且其編碼中的第一個位元組仍與ASCII相容,這使得原來處理ASCII字元的軟體無須或只須做少部分修改,即可繼續使用。因此,它逐漸成為電子郵件、網頁及其他儲存或傳送文字的應用中優先採用的編碼。
  
Unicode和UTF-8之間的轉換關係表(X表示碼點佔據的位)

碼點的位數 碼點起值 碼點終值 位元組序列 Byte 1 Byte 2 Byte 3 Byte 4
7 U+0000 U+007F 1 0XXXXXXX
11 U+0080 U+07FF 2 110XXXXX 10XXXXXX
16 U+0800 U+FFFF 3 1110XXXX 10XXXXXX 10XXXXXX
21 U+10000 U+1FFFFF 4 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX
UTF-8編碼位元組含義
  • 對於UTF-8編碼中的任意位元組B,如果B的第一位為0,則B獨立的表示一個字元(ASCII碼),形如0XXXXXXX;
  • 如果B的第一位為1,第二位為0,則B為一個多位元組字元中的一個位元組(非ASCII字元);
  • 如果B的前兩位為1,第三位為0,則B為兩個位元組表示的字元中的第一個位元組,形如110XXXXX 10XXXXXX;
  • 如果B的前三位為1,第四位為0,則B為三個位元組表示的字元中的第一個位元組,形如1110XXXX 10XXXXXX 10XXXXXX;
  • 如果B的前四位為1,第五位為0,則B為四個位元組表示的字元中的第一個位元組,形如11110XXX 10XXXXXX 10XXXXXX 10XXXXXX。

      因此,對UTF-8編碼中的任意位元組,根據第一位,可判斷是否ASCII字元;根據前二位,可判斷該位元組是否為一個字元編碼的第一個位元組;根據前四位(如果前兩位均為1),可確定該位元組為字元編碼的第一個位元組,並且可判斷對應的字元由幾個位元組表示;根據前五位(如果前四位為1),可判斷編碼是否有錯誤或資料傳輸過程中是否有錯誤。

      下面以漢字“強”為例子,演示一下如何從Unicode轉換成對應的UTF-8編碼:
      首先,從中日韓漢字Unicode編碼表 查到“強”的Unicode碼為U+5F3A(對應二進位制表示為0101 1111 0011 1010),根據上文Unicode和UTF-8之間的轉換關係表可知,“強”字處在第3行範圍內,因此需要用3個位元組進行儲存,也即格式為“1110XXXX 10XXXXXX 10XXXXXX”。依次將“強”字對應的二進位制表示從後向前填入格式中的X,多出的位補0。最終得到“強”字的UTF-8編碼為11101010 10111100 10111010,轉換成十六進位制就是EABCBA。

優點:
  • ASCII是UTF-8的一個子集。因為一個純ASCII字串也是一個合法的UTF-8字串,所以現存的ASCII文字不需要轉換。為傳統的擴充套件ASCII字符集設計的軟體通常可以不經修改或很少修改就能與UTF-8一起使用。
  • UTF-8是可變長的編碼,可以節省儲存空間。
  • UTF-8字串可以由一個簡單的演算法可靠地識別出來。就是說,一個字串在任何其它編碼中表現為合法的UTF-8的可能性很低,並且可能性隨字串長度增長而減小。
缺點:
  • 與其他Unicode編碼相比,特別是UTF-16,在UTF-8中ASCII字元佔用的空間只有一半,可是在一些字元的UTF-8編碼佔用的空間就要多出1/3,特別是中文、日文和韓文(CJK)這樣的方塊文字。