Как я могу перебрать кодовые точки Юникода строки Java? - PullRequest
94 голосов
/ 07 октября 2009

Итак, я знаю о String#codePointAt(int), но он индексируется смещением char, а не смещением кодовой точки.

Я подумываю попробовать что-то вроде:

  • с использованием String#charAt(int) для получения char по индексу
  • проверка того, находится ли char в диапазоне с высоким содержанием суррогатов
    • Если это так, используйте String#codePointAt(int), чтобы получить кодовую точку, и увеличьте индекс на 2
    • , если нет, используйте указанное значение char в качестве кодовой точки и увеличьте индекс на 1

Но мои опасения

  • Я не уверен, будут ли кодовые точки, которые естественно находятся в диапазоне высоких суррогатов, сохраняться как два char значения или одно
  • это похоже на ужасно дорогой способ перебора символов
  • кто-то должен был придумать что-нибудь получше.

Ответы [ 4 ]

131 голосов
/ 07 октября 2009

Да, Java использует кодировку UTF-16-esque для внутренних представлений строк и, да, она кодирует символы вне базовой многоязычной плоскости ( BMP ), используя схему суррогатного материнства.

Если вы знаете, что будете иметь дело с символами за пределами BMP, то вот канонический способ перебора символов строки 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);
}
60 голосов
/ 06 января 2015

Java 8 добавлена ​​CharSequence#codePoints, которая возвращает IntStream, содержащий кодовые точки. Вы можете использовать поток непосредственно для их итерации:

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

или с циклом for, собирающим поток в массив:

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

Эти способы, вероятно, дороже, чем решение Джонатана Файнберга , но они быстрее для чтения / записи и разница в производительности, как правило, будет незначительной.

6 голосов
/ 15 февраля 2014

Я хотел бы добавить метод обхода, который работает с циклами foreach ( ref ), плюс вы можете легко преобразовать его в новый String # codePoints метод java 8 при переходе Ява 8:

Вы можете использовать его с foreach следующим образом:

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

Вот вспомогательный метод:

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 (который может использовать больше оперативной памяти, чем описанный выше подход):

 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).

5 голосов
/ 07 октября 2009

Итерации по кодовым точкам подаются как запрос функции в Sun.

См. Sun Bug Entry

Существует также пример того, как перебирать там строковые кодовые точки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...