10
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Posted at

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となる。

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

10
3
0

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
  3. You can use dark theme
What you can do with signing up
10
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?