ฉันจะวนซ้ำผ่านจุดรหัสยูนิโคดของสตริง Java ได้อย่างไร


106

ดังนั้นฉันจึงรู้เกี่ยวกับString#codePointAt(int)แต่มันถูกสร้างดัชนีโดยการcharชดเชยไม่ใช่โดยการชดเชยจุดรหัส

ฉันกำลังคิดจะลองทำสิ่งต่างๆเช่น:

  • ใช้String#charAt(int)เพื่อรับcharดัชนี
  • ทดสอบว่าcharอยู่ในช่วงตัวแทนสูงหรือไม่
    • ถ้าเป็นเช่นนั้นให้ใช้String#codePointAt(int)เพื่อรับจุดรหัสและเพิ่มดัชนีขึ้น 2
    • ถ้าไม่ใช้charค่าที่กำหนดเป็นจุดรหัสและเพิ่มดัชนีขึ้น 1

แต่ความกังวลของฉันคือ

  • ฉันไม่แน่ใจว่าจุดรหัสที่อยู่ในช่วงตัวแทนระดับสูงจะถูกจัดเก็บเป็นสองcharค่าหรือค่าเดียว
  • นี่ดูเหมือนเป็นวิธีที่แพงมากในการวนซ้ำผ่านตัวละคร
  • ใครบางคนต้องมีสิ่งที่ดีกว่า

คำตอบ:


143

ใช่ Java ใช้การเข้ารหัสแบบ UTF-16-esque สำหรับการแสดงสตริงภายในและใช่มันเข้ารหัสอักขระนอก Basic Multilingual Plane ( 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);
}

2
ส่วนจะ "แพง" หรือไม่นั้น ... ไม่มีวิธีอื่นใดใน Java แต่ถ้าคุณใช้เฉพาะสคริปต์ละติน / ยุโรป / ซิริลลิก / กรีก / ฮิบรู / อาหรับคุณก็แค่ s.charAt () ตามเนื้อหาใจ :)
Jonathan Feinberg

24
แต่คุณไม่ควร ตัวอย่างเช่นหากโปรแกรมของคุณแสดงผล XML และหากมีผู้ให้ตัวดำเนินการทางคณิตศาสตร์ที่คลุมเครือบางอย่างอาจทำให้ XML ของคุณไม่ถูกต้อง
หอยทากกล

2
offset = s.offsetByCodePoints(offset, 1);ผมจะได้นำมาใช้ มีประโยชน์ในการใช้offset += Character.charCount(codepoint);แทนหรือไม่?
Paul Groke

3
@Mechanicalsnail ฉันไม่เข้าใจความคิดเห็นของคุณ เหตุใดการส่งออก XML จึงทำให้คำตอบนี้ทำงานผิดปกติ
Gili

3
@Gili คำตอบก็ดี เขาอ้างถึงความคิดเห็นของ @Jonathan Feinberg ซึ่งเขาสนับสนุนให้ใช้charAt()ซึ่งเป็นความคิดที่ไม่ดี
RecursiveExceptionException

74

เพิ่ม Java 8 CharSequence#codePointsซึ่งส่งคืนจุดIntStreamที่มีรหัส คุณสามารถใช้สตรีมโดยตรงเพื่อทำซ้ำได้:

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

หรือด้วย for loop โดยรวบรวมสตรีมลงในอาร์เรย์:

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

วิธีเหล่านี้อาจมีราคาแพงกว่าโซลูชันของ Jonathan Feinbergsแต่จะอ่าน / เขียนได้เร็วกว่าและความแตกต่างของประสิทธิภาพมักจะไม่มีนัยสำคัญ


3
for (int c : (Iterable<Integer>) () -> string.codePoints().iterator())ยังใช้งานได้
saka1029

2
รุ่นที่สั้นกว่าเล็กน้อยของ @ saka1029: s code:for (int c : (Iterable<Integer>) string.codePoints()::iterator) ...
Lii

7

การทำซ้ำจุดรหัสจะถูกยื่นเป็นคำขอคุณลักษณะที่ Sun

ดู Sun Bug

นอกจากนี้ยังมีตัวอย่างวิธีการวนซ้ำบน String CodePoints ที่นั่น


6
Java 8 ตอนนี้มี codePoints () วิธีการที่สร้างขึ้นใน String: docs.oracle.com/javase/8/docs/api/java/lang/…
Dov Wasserman

7

คิดว่าฉันจะเพิ่มวิธีการแก้ปัญหาที่ใช้ได้กับ foreach loops ( ref ) รวมทั้งคุณสามารถแปลงเป็นวิธีString # codePointsใหม่ของ java 8 ได้อย่างง่ายดายเมื่อคุณย้ายไปที่ java 8:

คุณสามารถใช้กับ 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 อย่างปลอดภัย (การแสดงสตริงภายในของ java)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.