2009-10-06 8 views
88

私はString#codePointAt(int)について知っていますが、コードポイントのオフセットではなく、charオフセットでインデックスされています。Java文字列のUnicodeコードポイントをどのように反復処理できますか?

私はのような何かをしようと考えている:にString#codePointAt(int)を使用して、インデックス

  • charがそうならばcharhigh-surrogates range
    • であるかどうかをテスト取得するString#charAt(int)を使用して

      • コードポイントを取得し、インデックスを2ずつ増やします。
      • 場合は、指定したcharコードポイントとして値、および1

    によってインデックスをインクリメントすると、しかし、私の懸念は

    • ある私は高サロゲートの範囲内に自然にあるコードポイントとして保存されるかどうかわからないんだけど2つのchar値または1つの
    • これは、文字を繰り返すにはひどい高価な方法のようです。
    • 誰かが何か良いものを思い付かなければなりません。
  • 答えて

    116

    はい、Javaは、はい、それは代理出産方式を用いて基本多言語面(BMP)以外の文字をエンコードし、文字列の内部表現のためのUTF-16風エンコーディングを使用し、。

    あなたはBMP外の文字を扱うことになります知っている場合は、ここではJavaのStringの文字を反復する標準的な方法です。

    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に組み込まれている方法は他にありません。しかし、Latin/European/Cyrillic/Greek/Hebrew/Arabicスクリプトだけを扱っているのであれば、s.charAt()を心臓のコンテンツに追加するだけです。 :) –

    +18

    しかし、あなたはすべきではありません。例えば、あなたのプログラムがXMLを出力し、誰かがあいまいな数学演算子を与えた場合、あなたのXMLは突然無効になるかもしれません。 –

    +0

    @Jonathan Feinbergそれは私が思ったことです。しかし、ここでは特別な数学的なE. UTF-16が99%の時間で働いていますが、それは本当に痛いものです。特に問題が長い間隠されていた場合。 – Martin

    5

    反復処理のコードポイント以上は機能として提出されてサンでのリクエスト

    が文字列のコードポイントを反復処理する方法の例もありSun Bug Entry

    を参照してください。

    +3

    Java 8にcodePoints()メソッドがStringに組み込まれています: http://docs.oracle.com /javase/8/docs/api/java/lang/CharSequence.html#codePoints –

    +0

    Javaの代わりに使用できる回避方法については、私の回答も参照してください<8。その間にhttp://stackoverflow.com/a/ 21791059/32453 – rogerdpack

    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(); 
         } 
         }; 
        } 
        }; 
    } 
    

    あなただけ(上記のアプローチよりも多くのRAMを使用する場合があります)intの配列に文字列を変換したい場合は

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

    または交互に:あなたはこのようなforeachのでそれを使用することができます

    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

    関連する問題