Ruby で絵文字を含む文字列の長さをカウントしたい
1
2
3
4
|
str = " => " str.size => 3 |
なんでやねん!?
関西人の廣田です。
ちょこファことちょこっとファームでサーバーサイドエンジニアをやっています。
先日、ちょこファの機能追加で、絵文字を含む文字列の文字列長をカウントする必要が出てきました。
しかし普通にカウントすると上のように残念な結果になります。
Objective-C
で絵文字をいい感じにカウントするには、Qiitaの記事がとても参考になると思います。
Ojbective-Cで絵文字を見た目通りにカウントする
http://qiita.com/matsuokah/items/a435e3c86318a793d307
本記事では、先のリンク先を参考にRuby
で絵文字の長さを正しくカウントしていきたいと思います。
絵文字に使われる特殊文字たち
絵文字には以下の5種類の特殊文字が使われています。
- Regional Character
- Skin tone modifier
- Variation selector
- Zero-width Joiner
- KeyCap
これらが入ることで余計な文字がカウントされてしまいます。
基本的にはそれぞれのUnicode
を正規表現で拾ってきてよしなにカウントすればOKです。
1つずつ、具体的に見てみましょう。
Regional Character
国旗の絵文字を定義するために使われる文字です。Unicode
の0xdde6
から0xddff
で定義されています。
これらはいわば特殊なアルファベットで、例えば??なら0xddef
(特殊な“J”)と0xddf5
(特殊な“P”)の2文字で定義されます。
そのため、すべての国旗の絵文字は文字列長2でカウントされます。
これを回避するには、Regional Character
が2文字続けて出現した時は適当な1文字に置き換えてやればOKです。
str = "??????" str.size => 6 str2 = str.gsub(/[\u{1F1E6}-\u{1F1FF}]{2}/, "0") str2.size => 3
いい感じですね???
Skin tone modifier
顔や人の絵文字の肌の色を変えるための文字です。
通常の顔の絵文字にはつかないのですが、肌の色が違う場合は通常の文字の後ろにこのSkin tone modifier
がついて、合わせて2文字でカウントされます。0xdffb
から0xdfff
で5種類の肌の色を表現しているので、これらを消してしまえばOKです。
str = "???" str.size => 3 str2 = str.gsub(/[\u{1F3FB}-\u{1F3FF}]/, "") => "??" str2.size => 2
にっこりです???
Variation selector
同じ記号でも違う表示をしたい時に使われる文字です。
例えば❤︎と
この2文字、本体のハートの部分は0x2764
で定義されているのですが、その後ろに0xfe0f
がつくとValiation selector
は0xfe00
から0xfe0f
の16種が定義されているので、あとはSkin tone modifier
と一緒ですね。
Zero-width Joiner
iOSやOS Xで家族の絵文字を定義するのに使われます。
実は家族の絵文字の中身は家族の人数分の人の絵文字で、それらの間にこのZero-width Joiner
が挟まれると家族の絵文字と認識されます。
つまり、4人家族の絵文字なら7文字分カウントされるというわけですね。
あと、家族の絵文字はApple製品でしか定義されていないというのが肝で、Androidなどから見ると家族の人数分の人の絵文字が普通に表示されるだけになってしまいます。
そのため、今回は家族の絵文字は1文字ではなく、その家族の人数分の文字列長として扱うことにします。Zero-width Joiner
は0x200d
で定義されているので、これを消してやればOKです。
str = "????" str.size => 7 str2 = str.delete("\u{200D}") => "????" str2.size => 4
おけまる???
KeyCap
長くなりましたがラストです。
最後はKeyCap
、囲み文字です。0x20e3
で定義されており、数字の絵文字を表現するのに使われています。
数字の絵文字は普通の半角数字の後ろにこのKeyCap
をつけて表現しています。
また、数字の絵文字にはもう一点注意があり、Apple製品では半角数字 + 0xfe0f
(Valiation Selector) + KeyCap
で表現されます。
そのため、AndroidとApple製品、両方の数字の絵文字を正しくカウントするには以下のようにするのがいいでしょう。
1
2
3
4
5
6
|
str = " str.size => 9 str2 = str.gsub(/[ 0 - 9 ]\u{ FE0F }?\u{ 20E3 }/, "0" ) str2.size => 3 |
お疲れ様でした???
まとめ
今回の機能追加で、絵文字とついでに正規表現と仲良くなった気がします。
絵文字はなかなか闇の深い規格だと思いましたが、これでしばらくは楽しい絵文字コミュニケーションライフが送れそうです。
また、本記事の作成にあたって、先輩エンジニアの方々が絵文字入力対応を行ってくださいました。
ありがとうございます?