Bir Genişlik Kontrolü Bir Kanji Karakterini Bozdu

Bir isim terminal tablosuna girdi ve bozuk bir şekilde çıktı. Soyadı 𠮷田 idi.

İlk karakter yaygın olan 吉 değildir. 𠮷 (U+20BB7) karakteridir. Bu, gerçek Japon aile isimlerinde kullanılan nadir bir formdur. Tablo, bir sütuna sığması için hücreyi kısalttı. Bir isim yerine, bozuk bir karakter yazdırıldı. Kanji ikiye bölündü.

Hata, tek satırlık bir kısayolda gizliydi. Kod, metni gerçekten kısaltmadan önce, indekse göre kesmenin güvenli olduğuna karar verdi. Bu mantık, JavaScript'in metinleri işleme biçimi nedeniyle başarısız oldu.

Bir JavaScript metninin üç farklı uzunluğu vardır:

  • Kod birimi uzunluğu (Code unit length): "𠮷".length 2'dir. Bu, UTF-16 birimlerini sayar.
  • Kod noktası sayısı (Code point count): [..."𠮷"].length 1'dir. Bu, gerçek karakterleri sayar.
  • Görüntüleme genişliği (Display width): Bir terminalde kapladığı sütun sayısı 2'dir.

Düz İngilizce metinler için bu sayılar aynıdır. "abc" 3 birime, 3 noktaya ve 3 sütuna sahiptir. Çoğu kod, bu tesadüfün bir kural olduğunu varsayar.

𠮷 karakteri bu kuralı bozar. 2 kod birimine ve 2 sütuna sahiptir. Sayılar eşleşir ancak farklı nedenlerden dolayı. Kod, 2'nin 2'ye eşit olduğunu gördü ve metni indekse göre kesmek için hızlı bir yol kullandı.

Metni 3. indeksten kestiğinde, ilk tam karakteri ve ikincisinin sadece yarısını aldı. Bu durum geride tek başına bir surrogate bıraktı. Terminaller bunu bozuk bir kutu olarak gösterir.

漢 gibi yaygın Japonca karakterler güvenlidir. 1 kod birimine ve 2 sütuna sahiptirler. 1, 2'ye eşit olmadığı için kod, hatalı kısayoldan kaçınır. Hata yalnızca nadir karakterlerde ve emojilerde ortaya çıkar.

Bunu düzeltmek için şunları yapmalısınız:

  • Yüksek surrogate içeren metinleri reddetmek için hızlı yolu korumaya alın (guard).
  • Kod birimleri yerine tam kod noktalarına göre kırpın (trim).

Array.from(str) kullanmak bunu düzeltir çünkü kod noktası bazında yineleme (iterate) yapar. Karakteri tek bir bütün birim olarak ele alır.

Ders basit: asla bir birime göre ölçüp başka bir birime göre kesmeyin. Eğer görüntüleme genişliğini ölçüp kod birimi indeksine göre keserseniz, kullanıcılarınızın verilerini bozarsınız.

Kodunuzu nadir bir CJK karakteri veya bir emoji ile test edin. ASCII bu hataları size göstermeyecektir. Kodunuzun korktuğu girdiyi sağlamalısınız.

Kaynak: https://dev.to/greymothjp/a-width-check-said-the-string-was-safe-to-cut-it-split-a-kanji-in-half-4hjk

İsteğe bağlı öğrenme topluluğu: https://greymoth-jp.github.io/cjk-failure-corpus/