2 Şubat 2020 Pazar

String.chars() ve String.codePoints() Farkı

Giriş
Eski Unicode standardında her şey 16 bit karakterdi. Ancak daha sonra 32 bit haline geldi. chars() metodu eski 16 bit karakterler için. Yeni 32 bit karakterler için codePoints() metodunu kullanmak lazım. 32 bit karakterler ise şöyle temsil edilirler.
Nevertheless, Unicode has come up with a solution for still using 16 bits to represent these characters. This solution implies the following:

16-bit high surrogates: 1,024 values (U+D800 to U+DBFF)
16-bit low surrogates: 1,024 values (U+DC00 to U+DFFF)
Bunun Java ile ilgili kısmının açıklaması şöyle.
Java takes advantage of this representation and exposes it via a suite of methods such as codePointAt()codePoints()codePointCount(), and offsetByCodePoints()
Dolayısıyla tam Unicode çalışmak istiyorsak şöyle yaparız.
Calling codePointAt() instead of charAt()codePoints() instead of  chars(), and so on helps us to write solutions that cover ASCII and Unicode characters as well.
Verilen int'in 16 veya 32 Bit Olduğunu Anlamak
Character.toChars() metodu kullanılır. Eğer döndürülen string'in charCount() sonucu 2 ise 32 bit code point'tir.
Örnek
Şöyle yaparız
public Map<String, Integer> countDuplicateCharacters(String str) {
  Map<String, Integer> result = new HashMap<>();
  for (int i = 0; i < str.length(); i++) {
    // -- highlighted code start --
    int cp = str.codePointAt(i);
    String ch = String.valueOf(Character.toChars(cp));     
    if(Character.charCount(cp) == 2) { // 2 means a surrogate pair
      i++;
    }
    // -- highlighted code end --
    result.compute(ch, (k, v) -> (v == null) ? 1 : ++v);
  }
  return result;
}
1. chars metodu
String 16 bitlik int'ler halinde dolaşılır ve IntStream döner. Bu IntStream nesnesi mapToObj() ile char'a cast edilir.

Örnek
Şöyle yaparız
String str = ...;
IntStream  stream = str.chars();
Örnek
Şöyle yaparız.
String str = "300‌";
str.chars().forEach (System.out::println);
Çıktı olarak şunu alırız.
51
48
48
Örnek
Şöyle yaparız.
public Map<Character, Long> countDuplicateCharacters(String str) {
  Map<Character, Long> result = str.chars()
    .mapToObj(c -> (char) c)
    .collect(Collectors.groupingBy(c -> c, Collectors.counting()));
  return result;
}
2. codePoints metodu
String 32 bitlik int'ler halinde dolaşılır ve IntStream döner. Açıklaması şöyle
Java also added four additional methods related to the Unicode code points:

int codePointAt(int index) : Returns the Unicode point at the specified index.

int codePointBefore(int index) : Returns the Unicode point previous to the specified index.

int codePointAt(int index) : Returns the number of Unicode points in the specified text range.

Örnek
Şöyle yaparız.
String str = ...;
IntStream  stream = str.codePoints();
Örnek
Şöyle yaparız.
String word = "hello";
word.codePoints().forEach(System.out::println); // 104, 101, 108, 108, 111
Örnek - sort
String'in kendi içindeki karakterleri sıralamak istersek kullanabiliriz. String UTF-16 kullandığı için bir Unicode karakter iki tane char ile temsil edilebilir. Sadece tek bir karaktere bakarak sıralama yapmak doğru olmaz. Şu kod yanlış.
int[] utfCodes = {128513, 128531, 128557};
String emojis = new String(utfCodes, 0, 3);


char[] chars = emojis.toCharArray();
Arrays.sort(chars);
System.out.println("Sorted String: " + new String(chars));
Şöyle yapmak gerekir. codePoint() metodu IntStream nesnesi döner.
int[] utfCodes = {128531, 128557, 128513};
String emojis = new String(utfCodes, 0, 3);


int[] codePoints = emojis.codePoints().sorted().toArray();
System.out.println("Sorted String: " + new String(codePoints, 0, 3));

Örnek - Counting Duplicate Characters For Unicode
Şu kod Unicode için doğru değil
public Map<String, Long> countDuplicateCharacters(String str) {
  Map<String, Long> result = str.chars()
    .mapToObj(c -> (char)c)
    .collect(Collectors.groupingBy(c -> c, Collectors.counting()));
  return result;
}
Unicodeiçin şöyle yaparız
public Map<String, Long> countDuplicateCharacters(String str) {
  Map<String, Long> result = str.codePoints()
    .mapToObj(c -> String.valueOf(Character.toChars(c)))
    .collect(Collectors.groupingBy(c -> c, Collectors.counting()));
  return result;
}
3. codePointAt/codePointBefore metodları
String sınıfının codePointAt() ve codePointBefore() adlı iki tane daha metodu var. Metodların içi şöyle.
//Java 8 java.lang.String source code
public int codePointAt(int index) {
  if ((index < 0) || (index >= value.length)) {
    throw new StringIndexOutOfBoundsException(index);
  }
  return Character.codePointAtImpl(value, index, value.length);
}
//...
public int codePointBefore(int index) {
  int i = index - 1;
  if ((i < 0) || (i >= value.length)) {
    throw new StringIndexOutOfBoundsException(index);
  }
  return Character.codePointBeforeImpl(value, index, 0);
}
Bu metodlar Character sınıfının codePointAtImpl() ve codePointBeforeImpl() metodlarını kullanıyorlar. Bu metodların içinde String'in tüm verisinin tarandığı ve highSurrogate lowSurrogate mantığının dikkate alınarak String içindeki bir önceki bir sonraki Unicode karakterinin döndürüldüğü görülebilir.
//Java 8 java.lang.Character source code
static int codePointAtImpl(char[] a, int index, int limit) {
  char c1 = a[index];
  if (isHighSurrogate(c1) && ++index < limit) {
    char c2 = a[index];
    if (isLowSurrogate(c2)) {
      return toCodePoint(c1, c2);
    }
  }
  return c1;
}
//...
static int codePointBeforeImpl(char[] a, int index, int start) {
  char c2 = a[--index];
  if (isLowSurrogate(c2) && index > start) {
    char c1 = a[--index];
    if (isHighSurrogate(c1)) {
      return toCodePoint(c1, c2);
    }
  }
  return c2;
}
Örnek
Açıklaması şöyle.
For example, the well-known two hearts symbol is a Unicode surrogate pair that can be represented as a char[] containing two values: \uD83D and \uDC95. The code point of this symbol is 128149. To obtain a String object from this code point, call  String str = String.valueOf(Character.toChars(128149)). Counting the code points in  str  can be done by calling str.codePointCount(0, str.length()), which returns 1 even if the str length is 2. Calling str.codePointAt(0) returns 128149 and calling  str.codePointAt(1) returns 56469. Calling Character.toChars(128149) returns 2 since two characters are needed to represent this code point being a Unicode surrogate pair. For ASCII and Unicode 16- bit characters, it will return 1.

Hiç yorum yok:

Yorum Gönder