標籤:java string
Java字串是一系列的Unicode字元序列,但是,它卻常常被誤認為是char序列。於是,我們經常這樣來遍曆字串:
package testchar;public class TestChar2 {public static void main(String[] args) {String s = "\u0041\u00DF\u6771\ud801\uDC00";for(int i = 0; i < s.length(); i++) {System.out.println(s.charAt(i));}}}然後,得到了意料之外的結果:
A
?
東
?
?
之所以會這樣,是因為Unicode字元和Java的char類型不能等同起來。實際上,Java中的char類型能表示的字元只是Unicode字元的子集,因為char只有16位,也就是說,它只能表示65536(2的16次方)個字元,但實際的Unicode字元數超過這個數字。在Java中,用UTF-16編碼char和String中的字元,一個字元對應的編碼值被稱為一個代碼點。有的代碼點用16位編碼,被稱為一個代碼單元,像char表示的那些字元;有的代碼點用32位編碼,也就是用兩個連續的代碼單元編碼,如上文中的\ud801\uDC00。其實,我們遍曆一個字串,遍曆的是這個字串中所有代碼點,而
s.length()
返回的是字串s中代碼單元的個數。當i對應的代碼單元只是一個32位代碼點的一部分時,
s.charAt(i)
也就不能像我們希望的那樣工作了。
下面給出了幾種正確遍曆一個字串的方法:
package testchar;/** * 正確遍曆String * * @author yuncong * */public class TestChar {public static void main(String[] args) {String s = "\u0041\u00DF\u6771\ud801\uDC00";// 獲得字串中代碼點的數量int cpCount = s.codePointCount(0, s.length());for (int i = 0; i < cpCount; i++) {int index = s.offsetByCodePoints(0, i);int cp = s.codePointAt(index);if (!Character.isSupplementaryCodePoint(cp)) {System.out.println((char) cp);} else {System.out.println(cp);}}System.out.println("-------------------");for (int i = 0; i < s.length(); i++) {int cp = s.codePointAt(i);if (!Character.isSupplementaryCodePoint(cp)) {System.out.println((char) cp);} else {System.out.println(cp);i++;}}System.out.println("-------------------");// 逆向遍曆字串for(int i = s.length() - 1; i >= 0; i--) {int cp = 0;// 當i等於0的時候,只剩下一個代碼單元,不可能是輔助字元if (i == 0) {cp = s.codePointAt(0);System.out.println((char)cp);} else {// 只有在i大於0的時候才可以退,並且// 因為剩下的代碼單元大於2,所以接下// 來訪問的兩個代碼單元可能表示輔助// 字元;// 退一個代碼單元i--;cp = s.codePointAt(i);if (Character.isSupplementaryCodePoint(cp)) {System.out.println(cp);} else {// 如果cp不是輔助字元,就回到遍曆的正常位置i++;cp = s.codePointAt(i);System.out.println((char)cp);}}}}}
(天坑啊,部落格中不能出現Java中的輔助字元)
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
Java 正確遍曆字串