Java 字符編碼 ASCII、Unicode、UTF-8、代碼點和代碼單元


  1 ASCII碼

  統一規定英語字符與二進制位之間的關系。ASCII碼一共規定了128個字符的編碼。例如,空格“SPACE”是32(二進制00100000),大寫字母A是65(二進制01000001)。這128個符號(包括32個不能打印出來的控制符號)只占用了一個字節的后面7位,最前面的1位統一規定為0。

 

  2 非ASCII編碼

  表示非英語的其他語言時,128個符號是不夠的。例如,在法語中,字母上方有注音符號,無法用ASCII碼表示。於是,一些歐洲國家就決定:利用字節中閑置的最高位編入新的符號。例如,法語中的é的編碼為130(二進制是10000010)。這樣可以表示256個符號。

  但是,不同的國家有不同的字母,因此,哪怕它們都使用256個符號的編碼方式,代表的字母卻不一樣。例如,130在法語編碼中代表é,在希伯來語編碼中代表字母Gimel (ג),在俄語編碼中代表另一個符號。0-127表示的符號是一樣的,不一樣的是128-255這一段。漢字多達10萬左右,需要使用多個字節表示一個漢字。例如,簡體中文常見的編碼方式是GB2312,使用兩個字節表示一個漢字,所以理論上最多可以表示256x256=65536個漢字。雖然都是用多個字節表示一個符號,但是GB類的漢字編碼與下面的Unicode和UTF-8是沒關系的。

 

  3 Unicode

  世界上存在多種編碼方式,同一個二進制數字可以被解釋成不同的符號。之所以電子郵件經常出現亂碼,是因為發信人和收信人使用的編碼方式不一樣。作為所有符號的編碼,Unicode納入了世界上所有的符號,給予每一個符號一個獨一無二的編碼。它是一個龐大的集合,可以容納100多萬個符號。例如,U+0639表示阿拉伯字母Ain,U+0041表示英語的大寫字母A,U+4E25表示漢字嚴。具體的符號對應表,可以查詢http://www.unicode.org/或者漢字對應表

  Unicode的問題

  Unicode只是一個符號集,只規定了符號的二進制編碼,但沒有規定存儲方式。例如,漢字嚴的Unicode是十六進制數4E25,轉換成二進制數足足有15位(100111000100101)即需要2個字節。不同的符號需要的字節數量不同。存在如下2個問題:

  1 如何區別Unicode和ASCII?

  計算機怎么知道3個字節表示一個符號,而不是分別表示3個符號呢?

  2 容易出現空間浪費

  英文字母僅需一個字節。如果Unicode統一規定每個符號用3個或4個字節表示,那么存儲英文字母時會出現2個或3個字節全是0,浪費空間。

  於是,出現了Unicode的多種實現方式。

 

  4 UTF-8

  UTF-8是互聯網上使用最廣的一種Unicode實現方式。其他實現方式包括UTF-16(字符用2個字節或4個字節表示)和UTF-32(字符用4個字節表示)。UTF-8是一種可變長的編碼方式,使用1~4個字節表示一個符號,根據不同的符號調整字節數量。

  UTF-8的編碼規則:

  1 對於單字節的符號,字節的第一位設為0,后面7位是這個符號的Unicode編碼。因此,對於英語字母,UTF-8編碼和ASCII碼是相同的。

  2 對於n個字節的符號(n>1),第一個字節的前n位都設為1,第n+1位設為0,后面字節的前兩位均設為10。剩下的二進制位由這個符號的Unicode編碼從后向前依次填入,空位補0。

  編碼規則總結見下表,字母x表示可編碼的位:

  

  以漢字嚴為例,如何實現UTF-8編碼:

  已知嚴的Unicode是4E25(100111000100101),根據上表,4E25處在第3行的范圍內(0000 0800-0000 FFFF),所以嚴的UTF-8編碼需要3個字節,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。然后,從嚴的最后一個二進制位開始,依次從后向前填入格式中的x,空位補0。於是,嚴的UTF-8編碼是“11100100 10111000 10100101”,轉換成十六進制是E4B8A5。

 

  5 代碼點和代碼單元

  從Java5開始,代碼點(code point)是指與一個編碼表中的某個字符對應的代碼值。在Unicode標准中,代碼點采用十六進制書寫,並加上前綴U+,例如U+0041是字母A的代碼點。多語言中每個字符用16位表示,稱為代碼單元(code unit)。在Java中,char類型用UTF-16編碼描述一個代碼單元。使用代碼單元而非字符,與Unicode字符集的增補相關。替代區域(surrogate area)[U+D800~U+DBFF表示第一個代碼單元,U+DC00〜U+DFFF表示第二個代碼單元]。可以快速知道一個代碼單元是一個字符的編碼,還是一個輔助字符的第一或第二部分。例如,對於整數集合的數學符號,它的代碼點是U+1D568,是用兩個代碼單元U+D835和U+DD68編碼的。

  String的length方法得到的是UTF-16編碼對應的代碼單元數量,而非字符個數;charAt方法得到的是指定位置的代碼單元,而非字符。獲取字符個數需要調用codePointCount方法,獲取單個字符需要調用codePointAt方法。

  考慮字符串:Ƶ is the set of integers.

  使用UTF-16編碼表示Ƶ需要兩個代碼單元。執行char ch = sentence.charAt(1);語句得到的不是空格,而是第二個代碼單元Z。為了避免發生這種情況,請不要使用char類型。

 

  參考資料

  字符編碼筆記:ASCII,Unicode和UTF-8

  深入學習Java中的字符串,代碼點和代碼單元


免責聲明!

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



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