Wie kann ich die Unicode-Codepunkte einer Java-Zeichenfolge durchlaufen?

Ich weiß also über String#codePointAt(int) , aber es ist durch den char Offset indiziert, nicht durch den Codepoint-Offset.

Ich denke darüber nach etwas zu versuchen wie:

  • Verwenden von String#charAt(int) , um das String#charAt(int) an einem Index String#charAt(int)
  • Testen, ob das char im hohen Surrogatbereich liegt
    • Ist dies der String#codePointAt(int) , verwenden Sie String#codePointAt(int) , um den Codepunkt String#codePointAt(int) , und erhöhen Sie den Index um 2
    • Ist dies nicht der Fall, verwenden Sie den angegebenen char Wert als Codepunkt und erhöhen Sie den Index um 1

Aber meine Bedenken sind

  • Ich bin nicht sicher, ob Codepunkte, die natürlich im hohen Surrogatbereich liegen, als zwei char Werte oder eins gespeichert werden
  • Das scheint eine furchtbar teure Art zu sein, Charaktere zu durchlaufen
  • Jemand muss sich etwas besseres ausgedacht haben.

Ja, Java verwendet eine UTF-16-artige Codierung für interne Darstellungen von Zeichenfolgen, und ja, es codiert Zeichen außerhalb der Basic Multilingual Plane ( BMP ) mit dem Ersatzschema.

Wenn Sie wissen, dass Sie mit Zeichen außerhalb des BMP arbeiten, dann ist hier die kanonische Methode, um über die Zeichen eines Java-Strings zu iterieren:

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

Java 8 fügte CharSequence#codePoints die einen IntStream , der die Codepunkte enthält. Sie können den Stream direkt verwenden, um über sie zu iterieren:

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

oder mit einer for-Schleife, indem der Stream in ein Array gesammelt wird:

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

Diese Wege sind wahrscheinlich teurer als Jonathan Feinbergs Lösung , aber sie sind schneller zu lesen / schreiben und der performancesunterschied wird normalerweise unbedeutend sein.

Das Iterieren über Codepunkte wird als Feature-Anfrage bei Sun abgelegt.

Siehe Sun Bug Eintrag

Es gibt auch ein Beispiel, wie man dort über String CodePoints iterieren kann.

Ich dachte, ich würde eine Workaround-Methode hinzufügen, die mit foreach-Schleifen ( ref ) funktioniert, und Sie können sie einfach in die neue String # codePoints-Methode von java 8 konvertieren, wenn Sie zu java 8 wechseln:

 public static Iterable codePoints(final String string) { return new Iterable() { public Iterator iterator() { return new Iterator() { 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(); } }; } }; } 

Dann kannst du es mit foreach wie folgt verwenden:

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

Oder alternativ, wenn Sie nur eine Zeichenfolge in ein Array von int konvertieren möchten (was mehr RAM als der obige Ansatz verwenden könnte):

  public static List stringToCodePoints(String in) { if( in == null) throw new NullPointerException("got null"); List out = new ArrayList(); 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; }