search
LoginSignup
2

More than 1 year has passed since last update.

Organization

JavaScript で絵文字の文字数をカウントしたかった

JavaScript での絵文字を含む String の文字数カウントは結構ハードだった話。

問題の事象

😇 は直感的には1文字カウントされてほしいが、 length プロパティを参照すると以下のような結果になる。

'hoge'.length  // 4
'ほげ'.length  // 2
'😇'.length  // 2 ...!?

なぜ?

length は単に文字数を返しているわけではない
JavaScript の内部では文字列を UTF-16 形式で保持しており、 lentgh はこの単位のコード数を返している。
ASCII やひらがなは1つの 16 bit で表されるが、絵文字の多くは サロゲートペア を使って2つの 16 bit で表現される。

前述の例は UTF-16 で以下のように表現される。

String.fromCharCode('0x0068', '0x006F', '0x0067', '0x0065')  // hoge
String.fromCharCode('0x307B', '0x3052')  // ほげ
String.fromCharCode('0xD83D', '0xDE07')  // 😇

見ての通り 😇 はコード2つ分なので、 length プロパティの値は2となる。

'😇'.length を1文字としてカウントするには

スプレッド構文 を使って Array に変換した上で length を取れば1文字(1要素)扱いされる。

[...'😇'].length  // 1

これで解決?

文字コードの世界は広かった。
man shrugging と呼ばれる以下の絵文字は次のようにカウントされる。

'🤷‍♂️'.length  // 5 ...!?
[...'🤷‍♂️'].length  // 4 ...!?

この絵文字は 🤷 ( 0xD83E 0xDD37 )と ♂ ( 0x2642 )の組み合わせであり、内部的には以下のように表現される。

String.fromCharCode('0xD83E', '0xDD37', '0x200D', '0x2642', '0xFE0F')

0x200D は絵文字の結合用のコード、 0xFE0F絵文字バリエーション・シーケンスと呼ばれるプレーン文字と絵文字を識別するためのコードらしい。
🤷 は1文字として判定されるものの、後続のコードがそれぞれ1文字判定され、結果4となる。

これらの文字に関してはライブラリを使って対応できるようだが、現状ではシンプルな対応方法はなさそう...。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2