web-dev-qa-db-fra.com

Comment puis-je parcourir les points de code unicode d'une chaîne Java?

Je connais donc String#codePointAt(int) , mais il est indexé par l'offset char, pas par l'offset du point de code.

Je pense essayer quelque chose comme:

Mais mes préoccupations sont

  • Je ne sais pas si les points de code qui se trouvent naturellement dans la plage des substituts élevés seront stockés sous la forme de deux valeurs char ou d'une seule
  • cela semble être un moyen extrêmement coûteux de parcourir les personnages
  • quelqu'un doit avoir trouvé quelque chose de mieux.
94
rampion

Oui, Java utilise un encodage UTF-16-esque pour les représentations internes des chaînes, et, oui, il code des caractères en dehors du plan multilingue de base ( BMP ) en utilisant le schéma de maternité de substitution.

Si vous savez que vous aurez affaire à des caractères en dehors du BMP, voici la manière canonique d'itérer sur les caractères d'une chaîne 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);
}
134

Java 8 ajouté CharSequence#codePoints qui renvoie un IntStream contenant les points de code. Vous pouvez utiliser le flux directement pour les parcourir:

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

ou avec une boucle for en collectant le flux dans un tableau:

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

Ces moyens sont probablement plus chers que solution de Jonathan Feinbergs , mais ils sont plus rapides à lire/écrire et la différence de performances sera généralement insignifiante.

61
Alex

Je pensais que j'ajouterais une méthode de contournement qui fonctionne avec les boucles foreach ( ref ), plus vous pouvez la convertir en Java 8 nouveau String # codePoints facilement lorsque vous passez à Java 8:

Vous pouvez l'utiliser avec foreach comme ceci:

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

Voici l'aide 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();
        }
      };
    }
  };
}

Ou alternativement si vous voulez juste convertir une chaîne en un tableau d'int (qui pourrait utiliser plus RAM que l'approche ci-dessus):

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

Heureusement, l'utilisation de "codePoints" gère en toute sécurité la paire de substitution de l'UTF-16 (représentation de chaîne interne de Java).

7
rogerdpack

L'itération sur les points de code est déposée en tant que demande de fonctionnalité chez Sun.

Voir Sun Bug Entry

Il existe également un exemple sur la façon d'itérer sur String CodePoints.

5
Alexander Egger