ニコニコ絵文字の罠 §
絵文字"😄"の1文字の長さをLengthプロパティで取得すると1文字であるにも関わらず2になります。
なぜでしょう?
C#の文字はchar型で表現されますが、これは事実上0~65535を表現する整数です。C#の文字列はUnicodeですが、これ以上の番号を持つUnicodeの文字はそのままでは表現できません。そこで、サロゲートペアと呼ばれる【2つの番号で1文字を表現する技術】で文字を拡張しています。比較的新しい時代に規格に登録された文字はほぼサロゲートペアを必要とする文字に含まれます。絵文字もそれに含まれます。
Unicodeには、この他に合成文字(複数の文字を合成して1文字を表現するもの)も存在し、正しく文字列の長さから実際の長さを得るのはかなり面倒です。
文字列はいちいち文字単位にバラさないで右から左に受け渡すようにしておくと安全です。ただし、サロゲートペアに含まれる番号は明確に定義されている(前半 U+D800 〜 U+DBFF、後半 U+DC00 〜 U+DFFF)ので、その番号の値は素通しすると決めていればchar単位にバラしてもあとで元に戻せば大丈夫です。
罠の数々 §
- 普通はこういう面倒なことはあまり考えなくてもプログラムは書ける。アルファベット、数字、基本的な記号類、ひらがな、カタカナ、基本的な漢字など、普段使う文字はほとんど1文字=Lengthが1と考えて良い。しかし、絵文字が入ってくるとこの原則は崩れてしまう
- 特殊な漢字、中国語などが入ってくる場合は要チェック
- 他に合成文字などもあって、本格的に全ての機能を正しく実装しようと思うとかなり面倒。よくサロゲートペアを「なぜ存在するのか分からない最悪の発明」「UTF-8があればいい」という意見を見かけるが、残念ながらサロゲートペアの他にも罠は多い。1文字=Lengthが1にはならないのである。
- 実は既定の設定だとコンソールアプリで絵文字を表示できない。日本の既定の設定だとコンソールはシフトJIS(CP932)で動作しているからだ。この場合、Unicodeの多くの文字は?や??に化ける。
参考リンク §
String.Length プロパティ
上記のページを見に行くと最初に【現在の String オブジェクト内の文字数を取得します。】と書いてあって単純に【文字数が分かる】かのように思えるが、注釈でそうではないことを示している。上記ページの注釈まできちんと読んでみよう。
リポジトリ §
https://github.com/autumn009/cshowto
CharLength §
using System;
class Program
{
static void Main()
{
var s = "😄";
Console.WriteLine(s.Length);
}
}
実行結果
2