Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
1
Help us understand the problem. What are the problem?

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

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
1
Help us understand the problem. What are the problem?