Làm cách nào để tôi có thể lặp qua các điểm mã unicode của một chuỗi Java?


105

Vì vậy, tôi biết về String#codePointAt(int), nhưng nó được lập chỉ mục bởi phần charbù, không phải bằng phần bù điểm.

Tôi đang nghĩ về việc thử một cái gì đó như:

Nhưng mối quan tâm của tôi là

  • Tôi không chắc liệu các điểm mã tự nhiên nằm trong phạm vi đại diện cao sẽ được lưu trữ dưới dạng hai chargiá trị hay một
  • đây có vẻ như là một cách tốn kém kinh khủng để lặp lại các ký tự
  • ai đó chắc hẳn đã nghĩ ra một cái gì đó tốt hơn.

Câu trả lời:


143

Có, Java sử dụng mã hóa UTF-16-esque cho các biểu diễn bên trong của Chuỗi và vâng, nó mã hóa các ký tự bên ngoài Mặt phẳng đa ngôn ngữ cơ bản ( BMP ) bằng cách sử dụng lược đồ thay thế.

Nếu bạn biết mình sẽ xử lý các ký tự bên ngoài BMP, thì đây là cách chuẩn để lặp lại các ký tự của Chuỗi 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);
}

2
Về việc nó có "đắt" hay không, thì ... không có cách nào khác được tích hợp sẵn trong Java. Nhưng nếu bạn chỉ xử lý các tập lệnh Latinh / Châu Âu / Cyrillic / Hy Lạp / Hebrew / Ả Rập, thì bạn chỉ cần s.charAt () với nội dung trái tim của bạn. :)
Jonathan Feinberg

24
Nhưng bạn không nên. Ví dụ: nếu chương trình của bạn xuất ra XML và nếu ai đó đưa cho nó một toán tử toán học khó hiểu nào đó, thì đột nhiên XML của bạn có thể không hợp lệ.
Ốc cơ học

2
Tôi sẽ sử dụng offset = s.offsetByCodePoints(offset, 1);. Có một số lợi ích trong việc sử dụng offset += Character.charCount(codepoint);thay thế?
Paul Groke

3
@Mechanicalsnail Tôi không hiểu nhận xét của bạn. Tại sao việc xuất ra XML lại khiến câu trả lời này hoạt động sai?
Gili

3
@Gili câu trả lời là ổn. Anh ấy đang đề cập đến bình luận của @Jonathan Feinberg, trong đó anh ấy ủng hộ việc sử dụng charAt()đó là một ý tưởng tồi
RecursiveExceptionException

72

Java 8 được thêm vào CharSequence#codePointstrả về một IntStreamchứa các điểm mã. Bạn có thể sử dụng luồng trực tiếp để lặp lại chúng:

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

hoặc với vòng lặp for bằng cách thu thập luồng vào một mảng:

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

Những cách này có lẽ đắt hơn giải pháp của Jonathan Feinbergs , nhưng đọc / ghi nhanh hơn và sự khác biệt về hiệu suất thường sẽ không đáng kể.


3
for (int c : (Iterable<Integer>) () -> string.codePoints().iterator())cũng hoạt động.
saka1029

2
Phiên bản ngắn hơn một chút của mã @ saka1029: s:for (int c : (Iterable<Integer>) string.codePoints()::iterator) ...
Lii,


7

Tôi nghĩ rằng tôi sẽ thêm một phương pháp giải quyết hoạt động với vòng lặp foreach ( ref ), ngoài ra bạn có thể chuyển đổi nó sang phương thức String # codePoints mới của java 8 một cách dễ dàng khi bạn chuyển sang java 8:

Bạn có thể sử dụng nó với foreach như sau:

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

Đây là mthod trợ giúp:

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

Hoặc thay thế nếu bạn chỉ muốn chuyển đổi một chuỗi thành một mảng int (có thể sử dụng nhiều RAM hơn so với cách tiếp cận ở trên):

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

Rất may, việc sử dụng "codePoints" xử lý an toàn cặp thay thế của UTF-16 (biểu diễn chuỗi nội bộ của java).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.