[java] Java String의 유니 코드 코드 포인트를 어떻게 반복 할 수 있습니까?

그래서 나는에 대해 알고 String#codePointAt(int)있지만 char코드 포인트 오프셋이 아니라 오프셋에 의해 인덱싱됩니다 .

나는 다음과 같은 것을 시도하는 것에 대해 생각하고 있습니다.

  • 인덱스 String#charAt(int)를 얻기 위해 사용char
  • 대리자char높은 범위 에 있는지 테스트
    • 그렇다면 String#codePointAt(int)코드 포인트를 가져오고 인덱스를 2 씩 증가시킵니다.
    • 그렇지 않은 경우 주어진 char값을 코드 포인트로 사용 하고 인덱스를 1 씩 증가시킵니다.

하지만 내 걱정은

  • 당연히 높은 대리 범위에있는 코드 포인트가 두 개의 char값 으로 저장되는지 아니면 하나의 값 으로 저장되는지 잘 모르겠습니다.
  • 이것은 문자를 반복하는 끔찍한 비용이 드는 방법처럼 보입니다.
  • 누군가 더 나은 것을 생각해 냈을 것입니다.


답변

예, Java는 문자열의 내부 표현을 위해 UTF-16-esque 인코딩을 사용하며, 예, 대리 체계를 사용하여 BMP (Basic Multilingual Plane) 외부의 문자를 인코딩합니다 .

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


답변

코드 포인트를 포함하는 CharSequence#codePoints을 반환하는 Java 8이 추가되었습니다 IntStream. 스트림을 직접 사용하여 반복 할 수 있습니다.

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

또는 스트림을 배열로 수집하여 for 루프를 사용합니다.

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

이러한 방법은 Jonathan Feinbergs의 솔루션 보다 비용이 많이 들지만 읽기 / 쓰기 속도가 더 빠르며 성능 차이는 일반적으로 중요하지 않습니다.


답변

코드 포인트에 대한 반복은 Sun의 기능 요청으로 제출됩니다.

Sun 버그 항목 참조

거기에 String CodePoints를 반복하는 방법에 대한 예제도 있습니다.


답변

foreach 루프 ( ref ) 와 함께 작동하는 해결 방법을 추가하고 Java 8 로 이동할 때 Java 8의 새로운 String # codePoints 메서드로 쉽게 변환 할 수 있다고 생각했습니다 .

다음과 같이 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 (자바의 내부 문자열 표현)의 대리 쌍을 안전하게 처리합니다.


답변