Slide 1

Slide 1 text

フロントエンドとバックエン ドで「1文字」を揃えよう

Slide 2

Slide 2 text

自己紹介 てきめん ● https://tekitoh-memdhoi.info ● https://github.com/youkideari tai ● https://phpc.social/@youkide aritai ● PHP コミッター – Mbstring – Unicode オレ

Slide 3

Slide 3 text

皆に質問です ● 1文字とはなんでしょう?

Slide 4

Slide 4 text

1文字とは ● 1バイト ● 1コードポイント – 基本多言語面(BMP) – 追加多言語面(1面) – 追加漢字面(2面) – など ● 1書記素クラスター

Slide 5

Slide 5 text

1バイト ● ASCIIコードは7ビットを使う ● ISO-2022-JPは7ビットを切り替えて使う ● 8ビットはISO-8859シリーズで使われたりする

Slide 6

Slide 6 text

1コードポイント ● ここで言うコードポイントとは、Unicodeのコー ドポイント – U+0000 〜 U+10FFFF までの21ビットが入る – 16進数4桁のことを基本多言語面(Basic Multilingual Plane:BMP)という – U+10000〜U+1FFFFのことを「追加多言語面」という – U+20000〜U+2FFFFのことを「追加漢字面」という

Slide 7

Slide 7 text

なぜ21ビットなのか ● Unicodeは最初、16ビットで世界中の字を入れられるとしていたが、その目 論見が見事に失敗したことで、1面以降に文字を追加する羽目になった (Unicode 2.0) ● UTF-16ではBMPの中にサロゲートペアを作って対処した – 前16ビット(ハイサロゲート): U+D800〜U+DBFF (1024(2^10)コードポイント分) – 後ろ16ビット(ローサロゲート): U+DC00〜U+DFFF (1024(2^10)コードポイント 分) – この組み合わせが1048576文字(2^20)分であり、U+10000〜U+10FFFFまでの範 囲となった – なお、サロゲートペアはUTF-16のみの概念で、他文字コードで扱えない (UTF-8、UTF-32) ● 結果として21ビットの範囲となった。

Slide 8

Slide 8 text

UTF-16は極めて重要ではある ● サロゲートペアが必要になってしまったUTF-16だ が、JavaScriptやJavaでは内部文字コードは UTF-16である – となると、サロゲートペアの知識も必要になってくる – さっきの話を組み合わせると、第2面(追加漢字面)に存 在する漢字はサロゲートペアが必要ということになる ● サロゲートペアは2コードポイント消費するので2と返る ● 例えば「𠮷」(U+20BB7)は’𠮷’.lengthで2

Slide 9

Slide 9 text

21ビットを丸ごと収録したUTF-32 ● 32ビットあれば21ビットまるごと収録できると したのがUTF-32 ● 可変長でなくなったので解決したように見える ● 短所は21ビットに対して32ビットも使うため、 無駄が多いところ

Slide 10

Slide 10 text

絵文字の登場 ● Unicodeに絵文字が搭載されることになった ● まあ、良かったですよ ● 1面(追加多言語面)にも入るようになった ● それどころの話でもなくなった

Slide 11

Slide 11 text

複数コードポイント ● 🇯🇵 – 国旗は一つのコードポイントではない – 日本の国旗は U+1F1EF U+1F1F5の組み合わせ ● 👨‍👨‍👦‍👦 – 家族も同じく複数のコードポイント(+ZWJ)で成り立ってい る – 複数パターンが考えられる(この例では男×4の家族)ので コードポイントの説明は割愛

Slide 12

Slide 12 text

Zalgo text ● ウムラウト(¨)などの発音記号は無限にくっつけら れる – 正規化D(NKD、NKFD)を行うとアルファベットと発音 記号が分離する ● Zalgo textと言う、発音記号を無限にくっつける文 化がある – H̵̛͕̞̦̰̜͍̰̥̟͆̏͂̌͑ͅ ä̷͔̟͓̬̯̟͍̭͉͈̮͙̣̯̬͚̞̭̍̀̾͠m̴̡̧̛̝̯̹̗̹̤̲̺̟̥̈̏͊̔̑̍͆̌̀̚͝͝b̴̢̢̫̝̠̗̼̬̻̮̺̭͔̘͑̆̎̚ ư̵̧̡̥̙̭̿̈̀̒̐̊͒͑ r̷̡̡̲̼̖͎̫̮̜͇̬͌͘g̷̹͍͎̬͕͓͕̐̃̈́̓̆̚͝ẻ̵̡̼̬̥̹͇̭͔̯̉͛̈́̕r̸̮̖̻̮̣̗͚͖̝̂͌̾̓̀̿̔̀͋̈́͌̈́̋͜ ● https://en.wikipedia.org/wiki/Zalgo_text

Slide 13

Slide 13 text

日本語の漢字 ● 日本語における漢字は、苗字や地名がバラバラ – 名前は2999文字(2026年現在)しか使えない ● MJ文字といって、文字情報基盤によって一応標準化された – Unicodeでは16.0にて収録された – 表意文字たる漢字の難しいところ – 文字は交換可能であるべきだが、字自体にアイデンティティがあ る人もいる ● 苗字の漢字の細かいところが違うとなる(縮退) ● 正確な形自体は画像などで戸籍に保存されている – 詳細は https://www.digital.go.jp/policies/local_governments/c haracter-specification

Slide 14

Slide 14 text

異体字セレクタ ● 渡邉さんの邉のように、書体が違うものを収録 – 邉 邉 邉󠄁 邉󠄂 邉󠄃 邉󠄄 邉󠄅 邉󠄆 邉󠄈 邉󠄉 邉󠄊 邉󠄋 邉󠄌 邉󠄍 邉󠄎 – 異体字セレクタ(Variable Sequence)という ● U+E0100〜U+E01EFまでの範囲 – 葛󠄀城市(かつらぎし:奈良県)と葛飾区(かつしか く:東京都)のように、基底文字の葛は一緒で、違う のは異体字セレクタの番号(葛󠄀: U+845B U+E0100)

Slide 15

Slide 15 text

これらを何文字と扱うのか ● これらを何文字として扱うのか ● 書記素クラスターという概念がある – 書記素がもともとの概念で、見た目の1文字 – 絵文字も異体字セレクタも扱える – Zalgo textは微妙 ● 発音記号を無限に被せられるため、書記素クラスターで数えるのは 難しいのではないか? ● 絵文字も、家族の絵文字のように無限に被せられる – こっちはまだ絵文字のリストが存在する ● https://unicode.org/emoji/charts/full-emoji-list.html

Slide 16

Slide 16 text

JavaScriptでの扱い方 ● Intl.Segmenterを使う – 第二引数に’grapheme’を指定、イテレーターが使 える

Slide 17

Slide 17 text

コードポイントの測り方 ● JavaScriptでBMP外もコードポイントごとに測る場 合 for...of文や[...variable]構文が使える

Slide 18

Slide 18 text

PHPなど、バックエンドで扱う ● PHPではgrapheme_*関数が簡単 – grapheme_str_split関数を追加したので、arrayで書 記素クラスターを扱える

Slide 19

Slide 19 text

書記素クラスターの弱点 ● 弱点というか、考慮しないといけないこと – 1書記素クラスターにはコードポイントの上限はない ● 将来的な制限を作りたくないらしい – 書記素クラスターをWebアプリケーションとして使う場合 には入力の長さを測る必要がある ● コードポイント数 ● バイト数 – さもないと、「1書記素クラスター、200MBの 」「さっき 👨‍👨‍👧‍👦 のZalgo textのような無限に続く発音記号「ä̈̈̈̈̈̈̈̈̈̈̈̈̈̈̈̈̈̈̈̈̈̈̈̈̈̈̈̈̈ 」」などとい う「1文字」を入力可能にしてしまう

Slide 20

Slide 20 text

1文字をどう扱うか ● このように多様なので、アプリケーションによって1文字の要件を決めるべき ● 人の名前が重要な場合や絵文字を扱う場合には書記素クラスターが重要 – 戸籍には外字が含まれるので、Unicodeのコードポイントでも扱えない場合もある ● 行政事務標準文字の概念が出てくるところ ● ここまで来ると詳しくないので詳しい人が出てきてほしいところ ● パフォーマンスなどを考えたときにはコードポイントで数えるよう妥協する – 私の名前の「邉」は「邉󠄂」ですはできませんと妥協する ● つまり、IVSの対応まではできませんということ – ただし「𠮷」を始めとした追加多言語面ができないのは流石に頑張ったほうがいい ● サロゲートペアが必要だが、コードポイントでなんとかできる場面はなんとかする