システムで扱う文字数の数え方についての話し合いの場があった
個人的には過去の@kawasimaさんの記事を思い出した qiita.com
読み返すと物凄い情報量だったのと、今回いろいろ情報をいただいてアップデートできたので、文字数を数える
ということについて改めて自分なりにまとめておく
文字数を数えるということ
aあ𩸽👨👩👧👦
を4文字として扱いたいが、システムでこれをカウントするのは厄介という話
バイト数を数える
Shift_JISの世界では、半角文字は1バイト、全角文字は2バイトだったが、UTF-8ではそれらの文字は、1バイト~3バイトで表される
a
は 1バイトあ
は 3バイト
そのため、Shift_JISの世界で通用したバイトでの文字長の数え方はUTF-8では通用しない
コードポイントを数える
コードポイントはUnicodeで規定されている文字に割り振られた数字のこと
コードポイントは16進数表示で頭にU+を付けて用いられる
ではこのコードポイントの数を数えればよいかというとそういうわけでもない
サロゲートペア
実際このコードポイントをシステムで扱うためにはbyte配列にする必要があり、UTF-8などの符号化方式を用いてエンコーディングしてから使う
しかし、UTF-8でエンコーディングする場合、コードポイントが16進数で10000以上の文字は表現できない
そのため、UTF-8ではコードポイントのD800〜DBFFとDC00〜DFFFを組み合わせてこれを表現する仕組みになっている
この仕組みをサロゲートペアという
これによりUTF-8のシステムで扱う場合は2文字の扱いになってしまう
a
は UnicodeでUx61
が割り振られていて、16進数で10000未満なので、UTF-8でエンコーディングしても1文字あ
は UnicodeでUx3042
が割り振られていて、16進数で10000未満なので、UTF-8でエンコーディングしても1文字𩸽
は UnicodeでUx29E3D
が割り振られていて、16進数で10000以上なので、UTF-8でエンコーディングするとUxD867
とUxDE3D
の組み合わせで表現されるため、2文字
> "a".length < 1 > "あ".length < 1 > "𩸽".length < 2
「𠮷野家」の𠮷もサロゲートペア
Qiitaの記事にもあるように、JavaやJavaScriptで単にString.lengthメソッドを使って文字数精査をしてしまうと、𠮷は2文字と判定されてしまうので、3文字で入力してるのに、「3文字で入力してください」っていうエラーがでてしまうので注意が必要
結合文字
サロゲートペアの仕組みとは別に、あえてコードポイントを組み合わせてつくられている文字がある
例えば、👨👩👧👦 はUx1F468
Ux200D
Ux1F469
Ux200D
Ux1F467
Ux200D
Ux1F466
それぞれ、👨
👩
👧
👦
(Ux200D
はで表現)で JSのString.lengthでは11文字になる
人間が認識する文字(書記素)
人間が認識している1文字というのは書記素(Grapheme cluster)と呼ばれるらしい
aあ𩸽👨👩👧👦
を4文字として数えるのは書記素を数えるということ
コードポイントから書記素を取り出したい
コードポイントが書記素にどうなるかのルールがある
UAX#29
http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries
この仕様を元に文字を抽出すればよいが大変そう
プロジェクトで決めてしまう
それでも文字数の考え方は人によって違うかもしれないのでキリがない
なので「このシステムではこれを〇〇文字とカウントします!」と決めてしまうのが最良かも
String.lengthで数えるのが、一番シンプル
Rubyは何もしなくても"𠮷野家".length
で 3
となる
https://magazine.rubyist.net/articles/0025/0025-Ruby19_m17n.html
Tweitterと同じ数え方にするなら、twitter-textを利用するのがよいかも
https://github.com/twitter/twitter-text
UAX#29の仕様に基づいてカウントするなら
https://github.com/orisano/graphemesplit
まとめ
10文字以内の入力フォームでaあ𩸽👨👩👧👦
を入力した場合、JSのString.lengthでカウントすると15文字になるのでエラーになるが
エラーになったらなったで、ユーザーは不思議に思うかもしれないが、「絵文字ダメなのかな?」とか「別の言葉で試してみよう」とか、ユーザー側が柔軟に対応してくれるかもしれない
aあ𩸽👨👩👧👦
を4文字として扱うことに疲弊するかもしれないので、問題にならないならそちらに倒すのもあり
どうしてもダメなら何をもってカウントするかはしっかり決めておく必要がありそう