Slide 1

Slide 1 text

Unicodeどうしてる? PHPから見たUnicode 対応と他言語での対 応についてのお伺い

Slide 2

Slide 2 text

自己紹介 てきめん ● https://tekitoh-memdhoi.info ● @youkidearitai ● https://github.com/youkidearit ai ● PHPのコミッターしてます – mbstring拡張 – grapheme関数 ● サイボウズ株式会社にいます オレ

Slide 3

Slide 3 text

Unicodeがよい ● Unicodeで統一したので文字化けから解放され た! ● 絵文字も使える! 🎉 ● U+ABCD など、U+のあとに続く16進数を、1コード ポイントと呼んでいて、ブロックごとにまとまって世 界中の文字が収録されている

Slide 4

Slide 4 text

Unicodeの多様なところ ● 全世界の文字を使えるように努力している – 使えなくても使えるように各地で努力している ● 絵文字はじめ、異体字などで複数のUnicodeコード ポイントを使う時がある ● 左から右だけでなく、右から左へ流れる言語もサ ポートしている

Slide 5

Slide 5 text

複数のコードポイントについて ● Unicodeには1コードポイントごとに「次を読むか、 このコードポイントで終わるか」の情報がある ● そのため、「1文字」と「1コードポイント」は違うもの になる – 見た目の1文字を「書記素クラスター(grapheme cluster)」という

Slide 6

Slide 6 text

Grapheme cluster 書記素クラスター ● 🇯🇵は “ 🇯” と “ 🇵”で構成されている。 – 絵文字は時折複数のコードポイントで成り立っている ● 漢字も複数のコードポイントを含むときがあり、例えば 「邉」 – 邉 邉 邉󠄁 邉󠄂 邉󠄃 邉󠄄 邉󠄅 邉󠄆 邉󠄈 邉󠄉 邉󠄊 邉󠄋 邉󠄌 邉󠄍 邉󠄎 – コードポイントは U+9089 U+E0101 ● 異体字セレクタ: U+E0100 ~ U+E01EF – これをIVS(Ideographic Variation Sequence)と呼んでい る

Slide 7

Slide 7 text

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の出番…?

Slide 8

Slide 8 text

JavaのStringの仕様 ● JavaはStringがUTF-16 – 基本多言語面(U+0000~ U+FFFF)ではString.length()は 問題ない – 例えば、𠮷(U+20BB7)だと、2と 返ってくる ● JavaScriptと同じ理由 ● (補足)ICU4JというICUのJavaの ライブラリがある ● (くらいしかわからない) OpenJDK 25のJava簡素だな

Slide 9

Slide 9 text

RubyのString ● Stringオブジェクトに自身の文字エンコーディングの情報を 持っている – 例えば、Shift_JISとEUC-JPのStringオブジェクトを連結させよう とするとエラーとすることができるなど ● String.grapheme_clustersによって書記素クラスターに分 割できる ● String.each_grapheme_clusterで書記素クラスターごとに ループができる

Slide 10

Slide 10 text

PHPでのUnicode対応 ● PHPのstringはバイナリも扱えるバイト列の集合体 ● mbstring関数で対応 – mbstring関数はコードポイント単位での対応 ● PHP 8.4でgrapheme_str_split関数を発明 – 書記素クラスターごとに配列に分割する関数 – これ以降、grapheme関数を次々に開発している ● 逆にmbstring関数が入れられない – mb_levenshtein関数は満場一致の否決、grapheme_levenshtein関数の開発

Slide 11

Slide 11 text

SwiftはStringが書記素クラスター ● SwiftはStringがデフォルト書記素クラスター単位 – すごい! ● そのため、String.reverse()というメソッドが書記素 クラスター単位で逆順になる – 🇯🇵はきちんと国旗のままだし、邉󠄁 は異体字セレク タが左にはみ出るとかもない

Slide 12

Slide 12 text

ちょっとした罠 ● さて、これは何書記素クラスターでしょう? – 👨‍👩‍👧‍👧‍>> ● 多分、「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

Slide 13

Slide 13 text

書記素クラスターの実験:emoji bomb ● 見た目は 👨‍👦‍👦 ● 実際は10000000回繰り返し、ZWJ(U+200D)を挟むことで「1書記素クラス ター」としてカウントさせる ● 結果として200MB超の1書記素クラスターの が誕生 👨‍👦‍👦 – 仮に絵文字爆弾 とでも名付けましょうか、 💣️> Bomb Emoji 💣️> があるけど ● なお、スクリーンに表示するだけでクラッシュするため出せない

Slide 14

Slide 14 text

Emoji bomb: SwiftとPHPで比較 ● 先程のemoji_bomb.txtを読み込んで、それぞれ書記素クラス ターとコードポイントで比較 ● 双方ともに、同じ結果だった ● 59999999コードポイントでありながら、見た目1書記素クラスター である ● Unicodeの仕様上正しい挙動をしている…のか?

Slide 15

Slide 15 text

Emoji bomb: JavaScriptではどうか ● 先程のemoji_bomb.txtを読み込んで、それぞれ書記素ク ラスターとString Iterator、String.lengthで比較 ● 59999999コードポイント、見た目1書記素クラスターとなった ● String.lengthではサロゲートペアがあるため89999999

Slide 16

Slide 16 text

書記素クラスターの数え方 ● 通常、ICUライブラリを使う – unicode.orgからあるデータを使って手動でもできる ● UBreakIteratorを使ってループを行い、ループ回 数を数える – ここでコードポイントを数えるとかはしないですね – そもそもコードポイントの制限てあるのかな?

Slide 17

Slide 17 text

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 より

Slide 18

Slide 18 text

ZWJが無限にくっつけられる? ● https://unicode.org/Public/17.0.0/emoji/emoji-zwj-sequences.txt を挙げたけども、 これなどによる書記素クラスターのデータバリデーションが必要な気がしている – でないと無限にZWJでくっつけられると判定される – ICUでは以下のコードで判定ができそう ● 変数targetがemoji_bombの文字列

Slide 19

Slide 19 text

止まらない書記素クラスター ● 各国・各地域の文字を正しく扱うには、どうしても書記 素クラスターの概念は重要になってくる – さっきの邊の字のように、各国各地域にルールがある – たとえば、¨(ウムラウト)を無限につなげることもできたり – 書記素クラスターを使いこなすことはユーザーの違和感の 緩和につながる ● 解消とは書けない、国際的な妥協があるから(CJKの漢字統合 (Han Unification)とか)

Slide 20

Slide 20 text

¨(ウムラウト)をくっつける ● ä̈̈̈̈̈̈̈̈̈̈ ←こういうの(形はレンダリングによって変わる) – Zalgo textという、例: ư̵̧̡̥̙̭̿̈̀̒̐̊͒͑ H̵̛͕̞̦̰̜͍̰̥̟͆̏͂̌͑ͅ ä̷͔̟͓̬̯̟͍̭͉͈̮͙̣̯̬͚̞̭̍̀̾͠m̴̡̧̛̝̯̹̗̹̤̲̺̟̥̈̏͊̔̑̍͆̌̀̚͝͝b̴̢̢̫̝̠̗̼̬̻̮̺̭͔̘͑̆̎̚ r̷̡̡̲̼̖͎̫̮̜͇̬͌͘g̷̹͍͎̬͕͓͕̐̃̈́̓̆̚͝ẻ̵̡̼̬̥̹͇̭͔̯̉͛̈́̕r̸̮̖̻̮̣̗͚͖̝̂͌̾̓̀̿̔̀͋̈́͌̈́̋͜ ● 無限にくっつけることができる – SNSではこれで遊んでいる人もいる – 絵文字と同じく1書記素クラスターで大量のコードポイントを送り つけることもできたりする ● なお、PHPのフレームワークのSymfonyでこれによって修正されてたこと があった – https://github.com/symfony/symfony/pull/13527/files

Slide 21

Slide 21 text

書記素クラスターに実用上必要と思われる コードポイント数 ● 以下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

Slide 22

Slide 22 text

文字とはなにか ● 文字数がバイト単位なのか、コードポイント単位なのか、JSなどで内部がUTF-16 でサロゲートペアもかかわってくるのか、書記素クラスター単位なのかで変わって くる – そのうえで、「文字とはなにか」を決めたほうが良さそう ● アプリケーションの要件に合わせたほうが良さそう – 漢字の形を気をつけなければならないならば、書記素クラスターになってくる ● 人の名前、土地の名前などアイデンティティが重要になってくる… ● しかしながら、コードポイント単位でも許される場面が多そう – 書記素クラスターのパフォーマンスについて言及してませんでしたが、明らかに遅く なってしまう

Slide 23

Slide 23 text

プログラミング言語としては ● 書記素クラスターの対応をしなきゃならないと感じている ● 一方で、コードポイント単位の処理もできたほうが良さそうに感じ る ● どっちもおそらく必要で世界中のユースケースが必要になりそう ● 現状、見た限りではプログラミング言語間で大きく差はなさそう? ● 書記素クラスターはコードポイント長のバリデーションが必要? – PHPのコミッターなので、PoC考えてみようかな 🤔

Slide 24

Slide 24 text

Unicodeどうしてる? ● PHPのコミッターとして、またUnicodeの機能強化を行ってる身としては、他言語の 状況は知りたいところ ● 様々な国・地域の自然言語のほうも知りたいところ – 特にRtoLの言語(アラビア語など)のほうはどうなんだろう? – ロケール(LDML:Locale Data Markup Language)もあり一筋縄ではない ● 書記素クラスターだけでも国を超えると違うルールの場合もあり得るしとても自 由 – だから文字化けに悩まされることもなくなったのですが… – 書き手と受け手で本当に同じ字の形を共有できてるかはわからないが一応読める

Slide 25

Slide 25 text

まとめ ● 今どきのコンピューターの文字の使い方、もっと踏み込んで Unicodeの使い方を調べています ● 書記素クラスターでいけそうですが、注意点も多そうです – コードポイント単位でも要件によって十分かもしれないですね ● 書記素クラスターの対応に足りない関数・メソッドなどが各 プログラミング言語にあるかもしれないですね ● Unicodeどうしたらいいでしょうかね?