Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

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

#BuriKaigi

Avatar for てきめん tekimen

てきめん tekimen PRO

January 10, 2026
Tweet

More Decks by てきめん tekimen

Other Decks in Programming

Transcript

  1. 自己紹介 てきめん • https://tekitoh-memdhoi.info • @youkidearitai • https://github.com/youkidearit ai •

    PHPのコミッターしてます – mbstring拡張 – grapheme関数 • サイボウズ株式会社にいます オレ
  2. Grapheme cluster 書記素クラスター • 🇯🇵は “ 🇯” と “ 🇵”で構成されている。

    – 絵文字は時折複数のコードポイントで成り立っている • 漢字も複数のコードポイントを含むときがあり、例えば 「邉」 – 邉 邉 邉󠄁 邉󠄂 邉󠄃 邉󠄄 邉󠄅 邉󠄆 邉󠄈 邉󠄉 邉󠄊 邉󠄋 邉󠄌 邉󠄍 邉󠄎 – コードポイントは U+9089 U+E0101 • 異体字セレクタ: U+E0100 ~ U+E01EF – これをIVS(Ideographic Variation Sequence)と呼んでい る
  3. 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の出番…?
  4. JavaのStringの仕様 • JavaはStringがUTF-16 – 基本多言語面(U+0000~ U+FFFF)ではString.length()は 問題ない – 例えば、𠮷(U+20BB7)だと、2と 返ってくる

    • JavaScriptと同じ理由 • (補足)ICU4JというICUのJavaの ライブラリがある • (くらいしかわからない) OpenJDK 25のJava簡素だな
  5. PHPでのUnicode対応 • PHPのstringはバイナリも扱えるバイト列の集合体 • mbstring関数で対応 – mbstring関数はコードポイント単位での対応 • PHP 8.4でgrapheme_str_split関数を発明

    – 書記素クラスターごとに配列に分割する関数 – これ以降、grapheme関数を次々に開発している • 逆にmbstring関数が入れられない – mb_levenshtein関数は満場一致の否決、grapheme_levenshtein関数の開発
  6. ちょっとした罠 • さて、これは何書記素クラスターでしょう? – 👨‍👩‍👧‍👧‍>> • 多分、「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
  7. 書記素クラスターの実験:emoji bomb • 見た目は 👨‍👦‍👦 • 実際は10000000回繰り返し、ZWJ(U+200D)を挟むことで「1書記素クラス ター」としてカウントさせる • 結果として200MB超の1書記素クラスターの

    が誕生 👨‍👦‍👦 – 仮に絵文字爆弾 とでも名付けましょうか、 💣️> Bomb Emoji 💣️> があるけど • なお、スクリーンに表示するだけでクラッシュするため出せない
  8. 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 より
  9. ¨(ウムラウト)をくっつける • ä̈̈̈̈̈̈̈̈̈̈ ←こういうの(形はレンダリングによって変わる) – Zalgo textという、例: ư̵̧̡̥̙̭̿̈̀̒̐̊͒͑ H̵̛͕̞̦̰̜͍̰̥̟͆̏͂̌͑ͅ ä̷͔̟͓̬̯̟͍̭͉͈̮͙̣̯̬͚̞̭̍̀̾͠m̴̡̧̛̝̯̹̗̹̤̲̺̟̥̈̏͊̔̑̍͆̌̀̚͝͝b̴̢̢̫̝̠̗̼̬̻̮̺̭͔̘͑̆̎̚

    r̷̡̡̲̼̖͎̫̮̜͇̬͌͘g̷̹͍͎̬͕͓͕̐̃̈́̓̆̚͝ẻ̵̡̼̬̥̹͇̭͔̯̉͛̈́̕r̸̮̖̻̮̣̗͚͖̝̂͌̾̓̀̿̔̀͋̈́͌̈́̋͜ • 無限にくっつけることができる – SNSではこれで遊んでいる人もいる – 絵文字と同じく1書記素クラスターで大量のコードポイントを送り つけることもできたりする • なお、PHPのフレームワークのSymfonyでこれによって修正されてたこと があった – https://github.com/symfony/symfony/pull/13527/files
  10. 書記素クラスターに実用上必要と思われる コードポイント数 • 以下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
  11. 文字とはなにか • 文字数がバイト単位なのか、コードポイント単位なのか、JSなどで内部がUTF-16 でサロゲートペアもかかわってくるのか、書記素クラスター単位なのかで変わって くる – そのうえで、「文字とはなにか」を決めたほうが良さそう • アプリケーションの要件に合わせたほうが良さそう –

    漢字の形を気をつけなければならないならば、書記素クラスターになってくる • 人の名前、土地の名前などアイデンティティが重要になってくる… • しかしながら、コードポイント単位でも許される場面が多そう – 書記素クラスターのパフォーマンスについて言及してませんでしたが、明らかに遅く なってしまう
  12. Unicodeどうしてる? • PHPのコミッターとして、またUnicodeの機能強化を行ってる身としては、他言語の 状況は知りたいところ • 様々な国・地域の自然言語のほうも知りたいところ – 特にRtoLの言語(アラビア語など)のほうはどうなんだろう? – ロケール(LDML:Locale

    Data Markup Language)もあり一筋縄ではない • 書記素クラスターだけでも国を超えると違うルールの場合もあり得るしとても自 由 – だから文字化けに悩まされることもなくなったのですが… – 書き手と受け手で本当に同じ字の形を共有できてるかはわからないが一応読める