Share
Embed
Transcript
25 slides
-
自己紹介 てきめん • https://tekitoh-memdhoi.info • @youkidearitai • https://github.com/youkidearit ai •
PHPのコミッターしてます – mbstring拡張 – grapheme関数 • サイボウズ株式会社にいます オレ -
Unicodeがよい • Unicodeで統一したので文字化けから解放され た! • 絵文字も使える! 🎉 • U+ABCD など、U+のあとに続く16進数を、1コード
ポイントと呼んでいて、ブロックごとにまとまって世 界中の文字が収録されている -
Grapheme cluster 書記素クラスター • 🇯🇵は “ 🇯” と “ 🇵”で構成されている。
– 絵文字は時折複数のコードポイントで成り立っている • 漢字も複数のコードポイントを含むときがあり、例えば 「邉」 – 邉 邉 邉󠄁 邉󠄂 邉󠄃 邉󠄄 邉󠄅 邉󠄆 邉󠄈 邉󠄉 邉󠄊 邉󠄋 邉󠄌 邉󠄍 邉󠄎 – コードポイントは U+9089 U+E0101 • 異体字セレクタ: U+E0100 ~ U+E01EF – これをIVS(Ideographic Variation Sequence)と呼んでい る -
JavaScriptのStringの仕様 • JavaScriptはStringがUTF-16 – 基本多言語面(U+0000~U+FFFF)ではString.lengthは問題ない – 例えば、𠮷(U+20BB7)だと、2と返ってくる • 1面(U+10000~U+1FFFF)以降サロゲートペアが必要なため、2面(U+20000 ~U+2FFFF)である𠮷はUTF-16では0xD842
0xDFB7となり、2となる – コードポイントを数えるのが結構大変 • String IteratorとSpread Operatorなどがある – こうなると書記素クラスターを使いたくなってくるよね? • Intl.Segmenterの出番…? -
JavaのStringの仕様 • JavaはStringがUTF-16 – 基本多言語面(U+0000~ U+FFFF)ではString.length()は 問題ない – 例えば、𠮷(U+20BB7)だと、2と 返ってくる
• JavaScriptと同じ理由 • (補足)ICU4JというICUのJavaの ライブラリがある • (くらいしかわからない) OpenJDK 25のJava簡素だな -
RubyのString • Stringオブジェクトに自身の文字エンコーディングの情報を 持っている – 例えば、Shift_JISとEUC-JPのStringオブジェクトを連結させよう とするとエラーとすることができるなど • String.grapheme_clustersによって書記素クラスターに分 割できる
• String.each_grapheme_clusterで書記素クラスターごとに ループができる -
PHPでのUnicode対応 • PHPのstringはバイナリも扱えるバイト列の集合体 • mbstring関数で対応 – mbstring関数はコードポイント単位での対応 • PHP 8.4でgrapheme_str_split関数を発明
– 書記素クラスターごとに配列に分割する関数 – これ以降、grapheme関数を次々に開発している • 逆にmbstring関数が入れられない – mb_levenshtein関数は満場一致の否決、grapheme_levenshtein関数の開発 -
ちょっとした罠 • さて、これは何書記素クラスターでしょう? – 👨👩👧👧>> • 多分、「3」と答えると思いますが、実は1 – 全部ZWJ(Zero Width
Joinner)でつなげてるため – 証拠に、カーソルが1文字分しか動かない • フォントに該当するグリフがないため – というか、フォント側がZWJを挟んだ全パターンを収録するのは不可能 – なので、Unicodeでemoji-zwj-sequences.txtという許可リストがある • https://unicode.org/Public/17.0.0/emoji/emoji-zwj-sequences.txt -
書記素クラスターの実験:emoji bomb • 見た目は 👨👦👦 • 実際は10000000回繰り返し、ZWJ(U+200D)を挟むことで「1書記素クラス ター」としてカウントさせる • 結果として200MB超の1書記素クラスターの
が誕生 👨👦👦 – 仮に絵文字爆弾 とでも名付けましょうか、 💣️> Bomb Emoji 💣️> があるけど • なお、スクリーンに表示するだけでクラッシュするため出せない -
Emoji bomb: SwiftとPHPで比較 • 先程のemoji_bomb.txtを読み込んで、それぞれ書記素クラス ターとコードポイントで比較 • 双方ともに、同じ結果だった • 59999999コードポイントでありながら、見た目1書記素クラスター
である • Unicodeの仕様上正しい挙動をしている…のか? -
Emoji bomb: JavaScriptではどうか • 先程のemoji_bomb.txtを読み込んで、それぞれ書記素ク ラスターとString Iterator、String.lengthで比較 • 59999999コードポイント、見た目1書記素クラスターとなった •
String.lengthではサロゲートペアがあるため89999999 -
Emoji bombからわかること • ユーザーからの入力を、書記素クラスター でバリデーションしようとしないほうが良 さげに見える – 絵文字とZWJのおかげで1文字に見えるの に、うん百メガバイトとか作れてしまう •
ただし、UAX #51 https://unicode.org/re ports/tr51/#valid-emoji-tag-sequence s によると、32コードポイントを上限として いる – それではなぜできるんだろう…?中の人に聞 いてみるか? • ICUライブラリで聞いてみます https://unico de-org.atlassian.net/browse/ICU-23302 There is one common constraint on valid emoji tag sequences: the entire emoji_tag_sequence, including tag_base and tag_end, must not be longer than 32 code points. https://unicode.org/reports/tr51/#valid-emoji-tag- sequences より -
止まらない書記素クラスター • 各国・各地域の文字を正しく扱うには、どうしても書記 素クラスターの概念は重要になってくる – さっきの邊の字のように、各国各地域にルールがある – たとえば、¨(ウムラウト)を無限につなげることもできたり – 書記素クラスターを使いこなすことはユーザーの違和感の
緩和につながる • 解消とは書けない、国際的な妥協があるから(CJKの漢字統合 (Han Unification)とか) -
¨(ウムラウト)をくっつける • ä̈̈̈̈̈̈̈̈̈̈ ←こういうの(形はレンダリングによって変わる) – Zalgo textという、例: ư̵̧̡̥̙̭̿̈̀̒̐̊͒͑ H̵̛͕̞̦̰̜͍̰̥̟͆̏͂̌͑ͅ ä̷͔̟͓̬̯̟͍̭͉͈̮͙̣̯̬͚̞̭̍̀̾͠m̴̡̧̛̝̯̹̗̹̤̲̺̟̥̈̏͊̔̑̍͆̌̀̚͝͝b̴̢̢̫̝̠̗̼̬̻̮̺̭͔̘͑̆̎̚
r̷̡̡̲̼̖͎̫̮̜͇̬͌͘g̷̹͍͎̬͕͓͕̐̃̈́̓̆̚͝ẻ̵̡̼̬̥̹͇̭͔̯̉͛̈́̕r̸̮̖̻̮̣̗͚͖̝̂͌̾̓̀̿̔̀͋̈́͌̈́̋͜ • 無限にくっつけることができる – SNSではこれで遊んでいる人もいる – 絵文字と同じく1書記素クラスターで大量のコードポイントを送り つけることもできたりする • なお、PHPのフレームワークのSymfonyでこれによって修正されてたこと があった – https://github.com/symfony/symfony/pull/13527/files -
書記素クラスターに実用上必要と思われる コードポイント数 • 以下2つのURL(UAX #51、UAX #15)から、32コードポイントを上限とするの が現実的 – https://unicode.org/reports/tr51/#valid-emoji-tag-sequences •
こっちは絵文字 – https://unicode.org/reports/tr15/#Stream_Safe_Text_Format • こっちは正規化(NKFD)の文面で出てくる • なお、自然言語で1書記素クラスター最多コードポイントはチベット語の 「Hakṣhmalawarayaṁ(ཧྐྵྨླྺྼྻྂ)」とのことで、9コードポイント(1基底文字+8結合 文字) – https://stackoverflow.com/questions/11978912/how-to-protect-again st-diacritics-such-as-zalgo-text -
文字とはなにか • 文字数がバイト単位なのか、コードポイント単位なのか、JSなどで内部がUTF-16 でサロゲートペアもかかわってくるのか、書記素クラスター単位なのかで変わって くる – そのうえで、「文字とはなにか」を決めたほうが良さそう • アプリケーションの要件に合わせたほうが良さそう –
漢字の形を気をつけなければならないならば、書記素クラスターになってくる • 人の名前、土地の名前などアイデンティティが重要になってくる… • しかしながら、コードポイント単位でも許される場面が多そう – 書記素クラスターのパフォーマンスについて言及してませんでしたが、明らかに遅く なってしまう -
プログラミング言語としては • 書記素クラスターの対応をしなきゃならないと感じている • 一方で、コードポイント単位の処理もできたほうが良さそうに感じ る • どっちもおそらく必要で世界中のユースケースが必要になりそう • 現状、見た限りではプログラミング言語間で大きく差はなさそう?
• 書記素クラスターはコードポイント長のバリデーションが必要? – PHPのコミッターなので、PoC考えてみようかな 🤔 -
Unicodeどうしてる? • PHPのコミッターとして、またUnicodeの機能強化を行ってる身としては、他言語の 状況は知りたいところ • 様々な国・地域の自然言語のほうも知りたいところ – 特にRtoLの言語(アラビア語など)のほうはどうなんだろう? – ロケール(LDML:Locale
Data Markup Language)もあり一筋縄ではない • 書記素クラスターだけでも国を超えると違うルールの場合もあり得るしとても自 由 – だから文字化けに悩まされることもなくなったのですが… – 書き手と受け手で本当に同じ字の形を共有できてるかはわからないが一応読める -
まとめ • 今どきのコンピューターの文字の使い方、もっと踏み込んで Unicodeの使い方を調べています • 書記素クラスターでいけそうですが、注意点も多そうです – コードポイント単位でも要件によって十分かもしれないですね • 書記素クラスターの対応に足りない関数・メソッドなどが各
プログラミング言語にあるかもしれないですね • Unicodeどうしたらいいでしょうかね?