Slide 1

Slide 1 text

文字とはなにか - PHPの文字コード処理について -

Slide 2

Slide 2 text

自己紹介 てきめん ● https://tekitoh-memdhoi.info ● X: @youkidearitai ● https://github.com/youkideari tai ● サイボウズ株式会社の正社員 オレ

Slide 3

Slide 3 text

コンピューターで文字を表すの基本 ● コンピューターは数値しか扱えません ● そこで、とある数値をAなどと紐づけていきました ● それが文字コードです ● 文字コードに照らし合わせて文字を表示してくれる のがフォントですね

Slide 4

Slide 4 text

Unicodeがよい ● ざっくり言ってしまうとUnicodeはいいぞ ● 文字化けから開放してくれたぞ!ってなる ● 絵文字も使えるぞ 🎉🎉

Slide 5

Slide 5 text

Unicodeって何? ● 世界中のすべての文字を収録しようというもの – ISO/IEC 10646 という工業規格があって、Unicodeと等 しくなるようになっています ● JIS X 0221が現在の日本の工業規格(JIS)での規格です – Unicodeのバージョンに伴って、収録されていく文字が 増えています

Slide 6

Slide 6 text

UnicodeとJIS X 0221との関係 Unicode ISO/IEC 10646 JIS X 0221 同期 日本版 ※各仕様書を読んだらこの図になりましたが「プログラマのための文字コード技術入 門」もほぼ同じ図になっていました

Slide 7

Slide 7 text

Unicodeのバージョンについて ● 現在最新版は15.1です。 ● 2024年ではUnicode 16.0に むけて作業が進められていま す – http://blog.unicode.org/ 2023/11/utc-177-highlig hts.html

Slide 8

Slide 8 text

絵文字の存在 ● バージョンなんてどうでも良くね?と思いがちです が、実はそうもいかないのです ● みなさんはこんなことはありませんか? – 私の推しマークは です、時々違う方がいます。気をつ 🩵 けてくださいなどと呼びかけているアイドルさん – 🙇 と♂♀が分割してた

Slide 9

Slide 9 text

スマートフォンは絵文字を入力しやすい ● スマートフォンならば簡単に絵文字を選んで入力す ることができます ● 新しいスマートフォンほど新しい絵文字を入力しやす くなります ● その一方で、大切に長くスマートフォンを使っている 人もいます

Slide 10

Slide 10 text

その結果起こること ● Unicodeのバージョンに気を使う必要があります – 古いスマートフォンでは絵文字が見えなかったり、分か れて見えたりします – 新しいスマートフォンでは当たり前のように新しい Unicodeのバージョンの絵文字が使えます

Slide 11

Slide 11 text

🩵はどうでしょうか ● https://emojipedia.org/ja/%E6%B0%B4%E8% 89%B2%E3%81%AE%E3%83%8F%E3%83%BC %E3%83%88 ● どうやらUnicode 15.0で入ったようです – 2023年現在の最新バージョンは15.1です – かなり新しい絵文字です – 古いスマートフォンでは見えないでしょうね…

Slide 12

Slide 12 text

🙇と♂♀が分割してた ● このケースの場合、土下座をしている人と♂や♀の マークが分割して見えることがあります ● 対応している機種であれば、「 」、「 」と表示され 🙇‍♂️ 🙇‍♀️ ます ● いきなりですがPHPでmb_strlenしてみましょう

Slide 13

Slide 13 text

mb_strlenした結果 ● 1文字のハズなのに、4とでましたね – つまり、4つのコードポイントがあるということになります – mb_str_splitもしてみましょう

Slide 14

Slide 14 text

mb_str_splitした結果 このように、4つのコードポイントに分かれていること がわかりますし、 と♂が別れています 🙇

Slide 15

Slide 15 text

コードポイントとは ● UnicodeでいうコードポイントとはU+1234などと 記す符号位置で、16進数で表します ● mbstringではこの単位で測っていきます

Slide 16

Slide 16 text

コードポイントは? 2コードポイント目がU+200D、4コードポイント目 がU+FE0Fです

Slide 17

Slide 17 text

それぞれの意味 ● U+200Dはゼロ幅接合子などと呼ばれてお り、Zero Width Joinerの略でZWJと言います ● U+FE0Fは異体字セレクタと言い、U+FE00から U+FE0Fまでの範囲16文字を使って絵文字のバリ エーションを表現します ● https://www.unicode.org/glossary/#variation _selector

Slide 18

Slide 18 text

mbstringでのUnicodeの対応方針 ● mbstringでは、(大体が)内部でUTF-32として扱 い、コードポイントごとに計算しています – 近頃ここで内部でUTF-8を使っている箇所があ り、統一されていないことを突っ込まれました。

Slide 19

Slide 19 text

異体字セレクタについて ● 漢字でも使われています – Ideographic Variation Sequence(漢字(表意文字)異体 字シーケンス、IVS)と呼ばれています – 範囲はU+E0100からU+E01EFです – 組み合わせを定義するのがIVS、字形を定義するデータ ベースをIVD(Ideographic Variation Database)といいま す – https://www.unicode.org/reports/tr37/

Slide 20

Slide 20 text

漢字の異体字セレクタについて ● 例えば「邉」 CJK UNIFIED IDEOGRAPH-9089 – https://glyphwiki.org/wiki/u908a-ue0104 – https://747.github.io/vsselector/#!/ja/9089 – 邉 邉󠄀 邉󠄁 邉󠄂 邉󠄃 邉󠄄 邉󠄅 邉󠄆 邉󠄈 邉󠄉 邉󠄊 邉󠄋 邉󠄌 邉󠄍 邉󠄎 … ● 游ゴシック体で表示させました ● すべて違う異体字です ● 同じ漢字に見えるのもあれば違うのもありますね

Slide 21

Slide 21 text

絵文字に戻っておさらいしましょう

Slide 22

Slide 22 text

mb_str_splitした結果 見えないほうはそのようにちがうわけですね ゼロ幅接合子 (ZWJ) U+200D (絵文字の)異体字セレクタ U+FE0F

Slide 23

Slide 23 text

どうすれば1文字として測れますか?

Slide 24

Slide 24 text

ICUというライブラリを使います ● Grapheme cluster(グラフィム、書記素クラスター)単位で測れば 良い – それを格納しているのがICU – 結局データベースから測らないといけないのです ● PHPではintl拡張に入っています – --enable-intlとしてコンパイルしましょう – grapheme_strlenを使えば測れます ● https://www.php.net/grapheme_strlen

Slide 25

Slide 25 text

PCRE(preg系関数)も使えます ● PCREも書記素クラス ター単位での検出がで きます ● \Xを使用します ● https://www.pcre.or g/original/doc/html/ pcrepattern.html

Slide 26

Slide 26 text

grapheme_strlenした結果 このようにして、書記素クラスターを正しく数えるこ とができるわけですね

Slide 27

Slide 27 text

濁音・半濁音の場合

Slide 28

Slide 28 text

アイヌ語の を考えてみましょう ト゚ ● ト゚ はアイヌ語のカタカナだそうです – トゥというみたいですね – Unicodeには単独のコードポイントは存在しません ● アイヌ語の濁音・半濁音はすべてこの様になっています ● したがって、「ト」と「゚」の組み合わせで表現します – U+30C8とU+309Aの組み合わせです – 本当は単独のコードポイントにしたかったそうですね (ユニコード戦記を参照ください)(JIS X 0213では1面5区94点)

Slide 29

Slide 29 text

を正確に測る ト゚ ● このように、grapheme_strlenを使って書記素クラ スターとして測ることになります。

Slide 30

Slide 30 text

本来の濁音と半濁音 ● JIS X 0201ではガのように文字と濁音が別々(いわゆる半角カ ナ) ● JIS X 0208ではガのように、独立した文字も収録された ● Unicodeでは独立した文字も別にできたりする – ガが果たしてU+30ACなのか、U+30ABとU+3099の両方なのかが一見 するとわからない – それを統一するのが「正規化」と呼ばれる

Slide 31

Slide 31 text

正規化の方法 ● 正規化方式D(NFD) – $ sapi/cli/php -r 'var_dump(Normalizer::normalize("ガ", Normalizer::NFD));' string(6) "ガ" # カと濁音が分割されている ● 正規化方式C(NFC) – $ sapi/cli/php -r 'var_dump(Normalizer::normalize("ガ", Normalizer::NFC));' string(3) "ガ" ● 正規化方式KD(NFKD) – $ sapi/cli/php -r 'var_dump(Normalizer::normalize("ガ", Normalizer::NFKD));' string(6) "ガ" # カと濁音が分割されている ● 正規化方式KC(NFKC) – $ sapi/cli/php -r 'var_dump(Normalizer::normalize("ガ", Normalizer::NFKC));' string(3) "ガ" ● PHPではintlの Normalizerクラスを使 います ● 正規化方式も4種あるの でその時適切な正規化 方式を選択する必要が あります

Slide 32

Slide 32 text

正規化で戸惑う例 ● 例えばハングルの「アニョハセ ヨ」(こんにちは)を正規化 D(NFD)をすると、ハングルの 音節がバラバラに分解されて しまいます(ちゃんと表示できる 場合もありますが) ● 参考: https://www.unicode.org/c harts/normalization/ \

Slide 33

Slide 33 text

まとめ ● Unicodeにはバージョンがあることがわかりました ● バージョンによって絵文字が表示されないことがわかりました ● ZWJ、異体字セレクタなどで必ずしも1コードポイントに収まらないことがわかりました ● 濁音・半濁音は色々な方法の組み合わせがあることがわかりました ● PHPのUnicodeの対応具合がわかりました – mbstringでは1コードポイントごと、intlとPCREでは書記素クラスターとして測れる ● 正規化は複雑すぎる、触れないでおければ幸せ ● Unicodeは知ることが多いことがわかり、わからないことがわかりました – たとえば、不正なバイトシーケンスとか喋ってないですね? – あなたが知っていることがあったら、教えてください!

Slide 34

Slide 34 text

提案 ● PHPのGrapheme関数には、文字列の処理関数が少ないように見 えます ● 少なくとも、mb_str_split相当の書記素クラスターごとにarrayで返 却できるとarray関数で処理できてよいのではと思いますがどうで しょう? ● つまり、grapheme_str_splitが必要なのではないかということです – 絵文字などを「一文字」として配列として分割するというものです – ポジティブなフィードバックをいただければRFCとPoCを作ろうかと思いま す

Slide 35

Slide 35 text

参照 ● http://www.unicode.org/L2/L2016/16181-gender-zwj-sequences.pdf ● https://ja.wikipedia.org/wiki/%E3%82%BC%E3%83%AD%E5%B9%85%E6%8E%A5 %E5%90%88%E5%AD%90 ● https://www.unicode.org/glossary/#variation_selector ● https://www.unicode.org/glossary/#ideographic_variation_sequence ● https://www.unicode.org/reports/tr37/ ● https://ja.wikipedia.org/wiki/%E7%95%B0%E4%BD%93%E5%AD%97%E3%82%B B%E3%83%AC%E3%82%AF%E3%82%BF#%E7%A8%AE%E9%A1%9E ● https://emojipedia.org/ja/emoji-15.0 ● https://747.github.io/vsselector/#!/ja/9089 ● https://glyphwiki.org/wiki/u908a-ue0104 ● https://www.php.net/grapheme_strlen