文字数とは
事の発端
ky-y. | Tools に文字数カウント機能が欲しい!
なぜ?
大学の文字数制限があるレポートを自動セーブ機能付きがあるWEBエディタで手軽に書きたい。
せっかくなら自分で作る(破滅の予感。
const text = "ホゲホゲ";
const count = text.length;
単純に.length
だと、日本語環境あるあるのマルチバイト非対応なのでダメ。
(もっというと、サロゲートペアにも非対応。
(PHPでいう、strlen
とmb_strlen
みたいな
const text = "ホゲホゲ";
const count = [...text].length;
調べてみると、「これで正確に文字数が計測できましたね!いかがでしたか?」系の記事が多い。
悪魔の刺客到来
👋🏻「やぁ。呼んだかい?」
👋🏻
を上記のコードテストすると、
const text = "👋🏻";
const count = [...text].length;
console.log(count); // 2 <- ????
最近の端末では、さまざまな観点から絵文字の肌の色といったものが選べるようになっている。
こういったものは技術的には、Full Emoji Modifier Sequencesといった名称で、Unicodeによって定義されている。
👋🏻
という絵文字の場合は、U+1F44B
とU+1F3FB
という2つのUnicodeからなっている。
(この時点で、「両方とも10000以上だと!!」と思ってる方めっちゃ鋭いです(何様
U+1F44B
は、👋🏻
の黄色版(というよりデフォルト)である👋
。
U+1F3FB
は、単体では表示されないと思うが肌の色を表している。
ここら辺の仕様がなかなかに複雑で、👋🏻
を1文字と判定する事は現況かなり困難。
サロゲートペアみたいに、「こことここがくっついてたら1文字!」みたいな規則がなくて自由度高すぎる弊害ここにあり。
今の所の唯一の打開策(といっても、Firefoxが対応してないけど)は、Intl.Segmenter
を使うこと。(参照:MDN)
要は、入力値を解析して書記素(目で見た時にこれが1文字だ!ってなる最小単位)を配列で返してくれる子。
(他にも単語分割もできるらしいけど精度等は未検証
(日本語でロケールを設定しているので、この設定のまま他言語の特殊な文字の挙動は未検証。
const text = "👋🏻";
const segmenter = new Intl.Segmenter("ja");
const count = segmenter.length;
console.log(count);
正確なのが嬉しい反面、そこそこ重いという問題も存在する。
自分の検証環境(後述)では、2500文字くらいから指数関数的に処理時間が遅くなる。
どうしても、文章を解析しているから仕方がない。
間違っても、onChangeで解析してはならない。
ブラウザのページがエラー画面になる案件が出てくる。
そこで、「そもそも、そこまで正確な書記素数が必要ですか?」という提案を個人的にはしたい。
これは下記いくつかの観点から出てくる提案である。
1, 書記総数厳密に制限して何になるの?
「書記素数を制限しないと、大量のデータが送られてくる」という心配事があるかもしれない。
しかしながら、この「大量のデータ」の大体の定義は「データ量」すなわち「バイト数」になるはずである。
それならば、書記素数ではなくバイト数等で制限地を設けた方が堅実であると考える。
(書記素数が1でも無限バイトになりうる実験を先人がしてくださっている。
2, Google Documentも諦めている(かどうかはわからんけど完璧に対応していない)案件
大学のレポートでよくお世話になるGoogle Document。
彼の文字数カウント機能で挙動を確認してみると、[...text].length
に極めて似た挙動を示すことが確認できる。(内部実装要確認)
なら、いっそのことそこまで厳密に書記素数は気にする必要ないんじゃないの?という考えに至った。
また新たな知識を学べて楽しかった。