なんとなく「文字数」ってシンプルに数えられると思ってたけど、実はそうでもないみたいなんだよね💭
例えば絵文字の「🤦🏼♂️」、これをJavaScriptで"🤦🏼♂️".length
ってやると、なんと7って返ってくるんだ😳
え?1じゃないの?ってびっくりしたんだけど、ちゃんと理由があって納得できる内容だったから、ちょっとシェアするね✨
そもそも文字列の「長さ」って何?
普通、日本語や英語の文字数は1文字ずつカウントできるよね。でもコンピューターの中では、文字はUnicode(ユニコード)っていう世界共通のルールで表されてて、それがちょっと複雑だったりするんだ🌸
たとえばアルファベットの「A」は1文字で1コードポイント(文字の単位)だけど、絵文字や特殊な文字は複数のコードポイントでできてることもあるの。
つまり、文字数=コードポイント数とは限らないってこと💡
「🤦🏼♂️」が7になるカラクリ
この「🤦🏼♂️」は、実は1つの絵文字に見えても、実はいくつかのパーツがくっついてできてるんだ👀
詳しく言うと…
- 🤦(顔を手で覆う人)
- 🏻(肌の色を表すトーン)
- ♂️(男性を示すジェンダー記号)
これらが複数のUnicodeコードポイントを組み合わせて1つの絵文字として表示されてるんだよね✨
JavaScriptのlength
はこれらのコードポイントの数をカウントするから、7個分になるのは間違いじゃないってわけ🥺
まとめ:見た目と中身は違うんだね
- 絵文字は複数のコードポイントの組み合わせでできてることが多い
- JavaScriptの
length
はコードポイントの数じゃなくて「UTF-16コードユニット」の数を返す - だから「🤦🏼♂️」みたいな複合絵文字は長くなるのは自然なこと
プログラミングの世界って、こういう「見た目と実態のズレ」がけっこうあるから面白いよね🥰
文字列の扱いも、ちょっと注意すると意外と深いんだなーって改めて思ったよ💭
気になる人は、Unicodeとか絵文字の仕組みを調べてみると、ちょっと楽しい発見があるかも✨
こういう裏話、知ってるとプログラム書くときの理解度が変わってくると思うよ👍
コメント
キンバリー
JSなら`Array.from("🤦🏼♂️").length`で5と数えられるよ、文字列イテレータはUTF-16コードポイントに依らないからね。
クリス
あの顔文字が長さ5なのは無駄じゃないよ、スカラー値を数えるのが唯一エンコーディングに依存しない正確な方法だから。
ハンナ
これもう2003年から言われてる話で、いい加減議論する必要ないでしょ。
ベン
ユーザー目線だと、この顔文字は長さ1で扱うべきなんだよ、カーソル移動は1回で済ませたいからね。
ミア
何を長さとして数えるか次第だよね、これは1文字で7バイトくらいかな、バイト長が知りたい時もあるし文字数が重要な時もある。
リリー
著者には賛同できないな、UTFコードポイントで長さを扱うのはちゃんと定義された単位を使ってるだけで、UTF-32セマンティクスを選ぶことじゃないよ。 JSを過剰に持ち上げPythonを悪く言いすぎ。 UTF-16は問題だらけで高レベル言語でエンコーディング依存の長さを選ぶのは意味がない。 内部処理はUTF-32が速いし、Pythonの方法は実用的で効率的なんだ。 結局、Unicodeコードポイントで長さを扱うのが一番意味があると思う。
ハンナ
この記事は伝えたいポイントを完全に外してる気がする。
ジョージ
答えは5でしょ。
レオ
その通り!
ワット
1,000文字のゼロ幅結合テスト中(壊したくないのでコメントは控えめに)
サム
Unicodeは間違った解決策だったよ、みんなロトカス語にして絵文字じゃなくて昔の顔文字に戻ろうぜ(笑)
ノーラン
ASCII最強、Unicodeとか面倒なだけ。
クロエ
ランダムアクセスのスカラー値は重要じゃないって意見だけど、自分はよくランダムアクセスするし、コストはO(1)だと思ってるよ。
ハンナ
なんで絵文字をUnicodeでサポートする必要があるのか全然わからない、グラフェムクラスタの概念は問題だらけで廃止すべき。 絵文字はSVGやMathMLみたいにアプリで処理すればいいし、Unicodeに無理やり入れる必要ないだろ。 多くの開発者はUTF-8でグラフェムだけ数えてて、Unicodeの複雑さは無視してるよ。 俺もグラフェムクラスタは無視して、そういう関数だけ作ってる。 Unicodeの設計には賛成してないし、変な仕様に付き合う必要なんてない。