Eine Breitenprüfung besagte, der String sei sicher zum Kürzen. Sie teilte ein Kanji in zwei Hälften.

Ein Name wurde in eine Terminal-Tabelle eingegeben und kam beschädigt wieder heraus. Der Nachname war 𠮷田.

Das erste Zeichen ist nicht das gebräuchliche 吉. Es ist 𠮷 (U+20BB7). Dies ist eine seltene Form, die in echten japanischen Familiennamen verwendet wird. Die Tabelle kürzte die Zelle, um in eine Spalte zu passen. Zurück blieb ein beschädigtes Zeichen.

Der Bug steckte in einer einzigen Zeile Code. Es war eine Optimierung, die entschied, dass ein String sicher nach Index gekürzt werden kann.

Ein JavaScript-String hat drei verschiedene Längen: • Code-Einheiten (.length): "𠮷".length ist 2. • Code-Points: [..."𠮷"].length ist 1. • Anzeige-Breite: 𠮷 nimmt 2 Spalten ein.

Bei standardmäßigem englischem Text sind all diese Zahlen identisch. Dieser Zufall lässt den Code sicher erscheinen.

Das Zeichen 𠮷 bricht diese Regel. Es hat 2 Code-Einheiten, da es ein Surrogate-Paar ist. Es nimmt 2 Spalten ein, da es ein breites Zeichen ist. Die Zahlen stimmen überein (2 = 2), aber aus unterschiedlichen Gründen.

Die Bibliothek cli-table3 nutzte einen Fast Path: Wenn die Länge der Code-Einheiten der Anzeige-Breite entspricht, dann kürze den String nach Index.

Das funktionierte jahrelang, weil gängige japanische Zeichen wie 漢 eine Länge von 1 und eine Breite von 2 haben. Sie lösten den Fast Path nie aus.

Der Fast Path wird nur bei seltenen Zeichen wie 𠮷 oder Emojis ausgelöst. Diese Zeichen haben eine Länge von 2 und eine Breite von 2. Der Code denkt, es handele sich um einfache Zeichen mit einer Einheit. Er kürzt sie nach Index in der Mitte. Dadurch bleibt ein einzelnes Surrogate-Element zurück. Deshalb zeigt das Terminal ein kaputtes Kästchen an.

Um dies zu beheben, müssen Sie:

  • Den Fast Path absichern, um Surrogate-Paare auszuschließen.
  • Statt nach Code-Einheiten nach Code-Points kürzen.

Die Verwendung von Array.from(str) hilft, da sie nach Code-Points iteriert. Dies stellt sicher, dass Sie ein Zeichen niemals in der Mitte kürzen.

Die Lehre ist einfach: Messen Sie niemals nach einer Einheit und kürzen Sie nach einer anderen. Wenn Sie die Anzeige-Breite oder Code-Points messen, müssen Sie auch mit denselben Einheiten kürzen.

Testen Sie Ihren Code mit einem CJK-Extension-B-Zeichen oder einem Emoji. ASCII wird diesen Bug niemals aufdecken.

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

Optionale Lern-Community: https://greymoth-jp.github.io/cjk-failure-corpus/