Bir Java Dizisinin unicode kod noktalarını nasıl yineleyebilirim?


105

Bu yüzden biliyorum String#codePointAt(int), ama charkod noktası ofsetiyle değil, ofsetle indeksleniyor .

Şöyle bir şey denemeyi düşünüyorum:

Ama endişelerim

  • Doğal olarak yüksek vekiller aralığında bulunan kod noktalarının iki chardeğer olarak mı yoksa bir değer olarak mı depolanacağından emin değilim
  • bu, karakterler arasında yinelemenin çok pahalı bir yolu gibi görünüyor
  • birisi daha iyi bir şey bulmuş olmalı.

Yanıtlar:


143

Evet, Java, Dizelerin dahili temsilleri için UTF-16-esque kodlaması kullanır ve evet, vekillik şemasını kullanarak Temel Çok Dilli Düzlem ( BMP ) dışındaki karakterleri kodlar .

BMP dışındaki karakterlerle uğraşacağınızı biliyorsanız, bir Java Dizesinin karakterleri üzerinde yineleme yapmanın kanonik yolu şu şekildedir:

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
"Pahalı" olup olmadığına gelince, şey ... Java'da yerleşik başka bir yol yoktur. Ancak, yalnızca Latince / Avrupa / Kiril / Yunanca / İbranice / Arapça alfabelerle uğraşıyorsanız, o zaman kalbinizin içeriğine göre sadece s.charAt () olursunuz. :)
Jonathan Feinberg

24
Ama yapmamalısın. Örneğin, programınız XML çıktısı veriyorsa ve birisi ona belirsiz bir matematiksel işleç verirse, aniden XML'niz geçersiz olabilir.
Mekanik salyangoz

2
Kullanırdım offset = s.offsetByCodePoints(offset, 1);. offset += Character.charCount(codepoint);Bunun yerine kullanmanın bir faydası var mı ?
Paul Groke

3
@Mechanicalsnail Yorumunuzu anlamıyorum. Neden XML çıktısı almak bu cevabın yanlış davranmasına neden olur?
Gili

3
@Gili cevap iyi. @Jonathan Feinberg'in charAt()kötü bir fikir olduğunu savunduğu yorumuna atıfta bulunuyordu
RecursiveExceptionException

72

Kod noktalarını içeren CharSequence#codePointsbir döndüren Java 8 eklendi IntStream. Üzerinde yinelemek için akışı doğrudan kullanabilirsiniz:

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

veya akışı bir dizide toplayarak bir for döngüsü ile:

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

Bu yollar muhtemelen Jonathan Feinbergs'in çözümünden daha pahalıdır , ancak okuması / yazması daha hızlıdır ve performans farkı genellikle önemsiz olacaktır.


3
for (int c : (Iterable<Integer>) () -> string.codePoints().iterator())ayrıca çalışır.
saka1029

2
@ Saka1029 biraz daha kısa sürümün: s kod:for (int c : (Iterable<Integer>) string.codePoints()::iterator) ...
Lii


7

Foreach döngüleri ( ref ) ile çalışan bir geçici çözüm yöntemi ekleyeceğimi düşündüm , ayrıca java 8'e geçtiğinizde bunu java 8'in yeni String # codePoints yöntemine kolayca dönüştürebilirsiniz :

Bunu foreach ile şu şekilde kullanabilirsiniz:

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

İşte yardımcı yöntem:

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

Veya alternatif olarak, bir dizeyi int dizisine dönüştürmek istiyorsanız (yukarıdaki yaklaşımdan daha fazla RAM kullanabilir):

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

Neyse ki "codePoints" kullanır, UTF-16'nın vekil çiftini (java'nın dahili dizgi gösterimi) güvenli bir şekilde işler.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.