Como posso iterar através dos pontos de código unicode de um Java String?

Então, eu sei sobre String#codePointAt(int) , mas ela é indexada pelo deslocamento de char , não pelo offset do código de código.

Estou pensando em tentar algo como:

  • usando String#charAt(int) para obter o char em um índice
  • testando se o char está na faixa de altos substitutos
    • em caso afirmativo, use String#codePointAt(int) para obter o codepoint e incremente o índice por 2
    • se não, use o valor char dado como o codepoint, e incremente o índice em 1

Mas minhas preocupações são

  • Não tenho certeza se os pontos de código que estão naturalmente na faixa de altos substitutos serão armazenados como dois valores char ou um
  • isso parece ser uma maneira muito cara de interagir entre os personagens
  • alguém deve ter inventado algo melhor.

Sim, Java usa uma codificação UTF-16-esque para representações internas de Strings e, sim, codifica caracteres fora do Plano Multilingual Básico ( BMP ) usando o esquema de sub-rogação.

Se você sabe que estará lidando com caracteres fora do BMP, então aqui está a maneira canônica de iterar os caracteres de um 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); } 

O Java 8 adicionou CharSequence#codePoints que retorna um IntStream contendo os pontos de código. Você pode usar o stream diretamente para iterar sobre eles:

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

ou com um loop for, coletando o stream em uma matriz:

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

Essas maneiras são provavelmente mais caras do que a solução de Jonathan Feinbergs , mas elas são mais rápidas de ler / escrever e a diferença de desempenho geralmente será insignificante.

A iteração de pontos de código é arquivada como uma solicitação de recurso na Sun.

Ver input do Bug do Sol

Há também um exemplo de como iterar os CodePoints de String lá.

Pensei em adicionar um método de solução alternativa que funcionasse com loops foreach ( ref ), além de poder convertê-lo para o novo método String # codePoints do java 8 facilmente ao mover para o java 8:

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

Então você pode usá-lo com foreach assim:

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

Ou alternadamente, se você quiser apenas converter uma string em uma matriz de int (que pode usar mais RAM do que a abordagem acima):

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