スマートキャンプの20卒エンジニアの高砂です!
皆さんは、JavaScriptにおける絵文字の扱われ方が難しい事をご存知ですか?
本記事では、その背景と適切な方法を解説していきます!
JavaScriptにおける絵文字の問題点
JavaScriptで文字数カウントを実装する際、素直に考えると下記のようなコードになるかと思います。
const text = 'おはよう😊' console.log(text.length)
しかし、実は「絵文字が含まれている文字列は.length
では適切にカウントできない」という問題点があるのをご存知でしょうか?
実際にご覧頂くのが早いと思うので、CodePen様をお借りして文字数カウントをしてくれるアプリを簡単に作ってみました。
以下に置いておきますので、本記事と併せてご自由にお試しください。
※ 尚、こちらは後述する方法でのカウントも併せて実装しております。
https://codepen.io/jonpili/pen/bGpbOWJcodepen.io
問題点の再現
このアプリ上で、絵文字が1つ入力された時はこのようにカウントされる事を想定していると思います。
しかし、別の絵文字を入力してみると…なにやらカウントがおかしくなっているようです。
更に他の絵文字にすると、カウントが更に想定より大きくなっています。
ここまで来ると、もはやカウントが意味を成していません。
このようにJavaScriptの.length
プロパティは、絵文字によっては想定と違う挙動を示してしまうという問題点があります。
問題点の背景
このようになる理由は、絵文字の多くが通常の文字の2倍以上のbit数で扱われているからです。
JavaScriptでは絵文字も含んだ文字をUTF-16という方式で保存していますが、例えば「お」「は」「よ」「う」のような通常の文字が16bitで表されているのに対し、「😊」は32bitで表されています。
JavaScriptのStringオブジェクトのプロパティである.length
では、前者は16bitで1文字分ですが、後者は32bitで2文字分として換算するのでカウントが想定通りに働かないという事になるんですね。
また、「👍🏻」は「👍」に肌の色の定義データを加えて64bit(ベースの絵文字32bit + 定義データ32bit)で4文字分、「👨👩👧👦」は「👨」「👩」「👧」「👦」とそれらを結合するデータから構成されている為に176bit(絵文字32bit × 4 + 結合データ16bit × 3)で11文字分としてカウントされています。
このように絵文字によって様々な理由はありつつも、多くの絵文字は2文字以上でカウントされてしまいます。
絵文字を適切に扱う方法
では、想定通りにカウントするにはどうすれば良いでしょうか?
その一番簡単な解決方法は下記のように書くことです。
const text = 'おはよう😊' console.log(Array.from(text).length)
これはES2015(ES6)以降で使える記法ですが、このようにすると基本的な絵文字については正しくカウントされるようになります。
[...text].length
という短く書ける記法もあるので、そちらでも構いません。
ただこの方法だと、「👍🏻」や「👨👩👧👦」といった特殊な絵文字には対応できません。
それらについても正しくカウントしたい場合、「カーソルが 1 つ移動する分」という定義に基づいてカウントできる下記のようなライブラリを導入するのが良いでしょう。 https://www.npmjs.com/package/graphemesplit
まとめ
JavaScriptにおける絵文字の扱われ方は複雑で、私も文字数カウントを実装する際は非常に悩みました…。
本記事が、お読みくださる方の開発の一助になれば幸いです。
尚、本記事では絵文字に焦点をあててお話しさせて頂きましたが、実は上記の方法で「𠮷」といった特殊文字のカウントも解決する事が可能です。
というわけで、JavaScriptにおいて文字数のカウントを実装する場合は、本記事でのやり方、もしくは上記ライブラリの導入をおすすめします!
それでは、お読みくださりありがとうございました!