web-dev-qa-db-ja.com

Java文字列のUnicodeコードポイントを反復処理するにはどうすればよいですか?

String#codePointAt(int) について知っていますが、コードポイントオフセットではなく、charオフセットでインデックス付けされています。

私は次のようなことをしようと考えています:

  • String#charAt(int) を使用して、インデックスでcharを取得します
  • char高代理範囲 にあるかどうかのテスト
    • その場合は、 String#codePointAt(int) を使用してコードポイントを取得し、インデックスを2ずつ増やします
    • そうでない場合は、指定されたchar値をコードポイントとして使用し、インデックスを1増やします

しかし、私の懸念は

  • 自然に上位サロゲート範囲にあるコードポイントが2つのchar値または1つの値として保存されるかどうかはわかりません
  • これは、キャラクターを反復処理するための非常に高価な方法のようです
  • 誰かがもっと良いものを思いついたに違いない。
94
rampion

はい、JavaはStringsの内部​​表現にUTF-16風のエンコーディングを使用します。そして、はい、Basic Multilingual Plane( [〜#〜] bmp [ 〜#〜] )代理スキームを使用します。

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

Java 8が追加されました CharSequence#codePoints は、コードポイントを含むIntStreamを返します。ストリームを直接使用して、それらを反復処理できます。

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

または、ストリームを配列に収集してforループを使用します。

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

これらの方法は、おそらく Jonathan Feinbergsのソリューション よりも高価ですが、読み取り/書き込みが高速であり、パフォーマンスの違いは通常わずかです。

61
Alex

Foreachループ( ref )で動作する回避方法を追加すると思いますが、さらにJava 8の新しい String#codePoints Java 8に移動すると簡単にメソッド:

次のようにforeachで使用できます:

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

ヘルパーmthodは次のとおりです。

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

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

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

ありがたいことに「codePoints」を使用すると、UTF-16(Javaの内部文字列表現)の代理ペアを安全に処理できます。

7
rogerdpack

コードポイントの反復処理は、Sunで機能要求として提出されます。

Sun Bug Entry を参照してください

また、String CodePointsを反復処理する方法の例もあります。

5
Alexander Egger