2009-10-06 79 views
88

所以我知道String#codePointAt(int),但它的索引char偏移量,而不是碼位移。如何迭代Java字符串的unicode代碼點?

我想嘗試喜歡的東西:

遞增索引,但我的擔心是

  • 我不知道是否該碼點自然在高代理範圍將被存儲爲兩個char值或一個
  • 這似乎是一個非常昂貴的方式來遍歷字符
  • 有人必須想出更好的東西。

回答

116

是,Java應用UTF-16式的編碼絃樂的內部表示,是的,它編碼使用代孕方案基本多文種平面(BMP)之外的字符。

如果你知道你將要處理與BMP之外的字符,那麼這裏就是遍歷一個Java字符串中的字符的正規途徑:

final int length = s.length(); 
for (int offset = 0; offset < length;) { 
    final int codepoint = s.codePointAt(offset); 

    // do something with the codepoint 

    offset += Character.charCount(codepoint); 
} 
+2

至於它是否「昂貴」,那麼......沒有其他方式構建到Java中。但是,如果您只處理拉丁/歐洲/西里爾/希臘/希伯來語/阿拉伯語腳本,那麼您只需將s.charAt()放入您的內容。 :) – 2009-10-06 20:25:32

+18

但你不應該。例如,如果你的程序輸出XML,並且如果有人給它一個模糊的數學運算符,突然你的XML可能是無效的。 – 2012-07-15 01:18:59

+0

@Jonathan Feinberg這就是我的想法。但是在這裏出現了特殊的數學E. UTF-16在99%的時間內工作 - 但後來變得非常痛苦。特別是當問題長期處於隱藏狀態時。 – Martin 2014-02-09 13:12:34

5

遍歷代碼點被申請爲特徵請求在Sun.

Sun Bug Entry

還有如何串碼點迭代有一個例子。

+3

現在Java 8已經內置了一個codePoints()方法來構建字符串: http://docs.oracle.com /javase/8/docs/api/java/lang/CharSequence.html#codePoints – 2014-04-18 17:13:25

+0

另請參閱我的答案,您可以在java <8的地方使用它的解決方法http://stackoverflow.com/a/ 21791059/32453 – rogerdpack 2014-12-09 20:02:56

4

想我會添加一個以foreach循環(ref)工作的變通辦法,再加上你可以當你移動到Java 8很容易轉換成Java 8的新的String#代碼點方法:

public static Iterable<Integer> codePoints(final String string) { 
    return new Iterable<Integer>() { 
    public Iterator<Integer> iterator() { 
     return new Iterator<Integer>() { 
     int nextIndex = 0; 
     public boolean hasNext() { 
      return nextIndex < string.length(); 
     } 
     public Integer next() { 
      int result = string.codePointAt(nextIndex); 
      nextIndex += Character.charCount(result); 
      return result; 
     } 
     public void remove() { 
      throw new UnsupportedOperationException(); 
     } 
     }; 
    } 
    }; 
} 

然後你可以用foreach使用它像這樣:

for(int codePoint : codePoints(myString)) { 
    .... 
} 

或者交替,如果你只是想將一個字符串轉換成int數組(這可能會使用更多的內存比上面的方法):

public static List<Integer> stringToCodePoints(String in) { 
    if(in == null) 
     throw new NullPointerException("got null"); 
    List<Integer> out = new ArrayList<Integer>(); 
    final int length = in.length(); 
    for (int offset = 0; offset < length;) { 
     final int codepoint = in.codePointAt(offset); 
     out.add(codepoint); 
     offset += Character.charCount(codepoint); 
    } 
    return out; 
    } 
46

Java 8添加了CharSequence#codePoints,它返回一個包含代碼點的IntStream。 您可以直接使用流來遍歷他們:

string.codePoints().forEach(c -> ...); 

或一個for循環通過收集流到一個數組:

for(int c : string.codePoints().toArray()){ 
    ... 
} 

這些方法可能比Jonathan Feinbergs's solution更昂貴,但他們讀/寫速度更快,性能差異通常不顯着。

+0

'for(int c:(Iterable )() - > string.codePoints()。iterator())'也可以。 – saka1029 2017-07-12 23:13:18