さて、.NetとC#では、すべての文字列はUTF-16LEとしてエンコードされます。A string
は文字のシーケンスとして格納されます。それぞれchar
が2バイトまたは16ビットのストレージをカプセル化します。
「紙または画面上」で単一の文字、文字、グリフ、記号、または句読点として見えるものは、単一のテキスト要素と考えることができます。Unicode Standard Annex#29 UNICODE TEXT SEGMENTATIONで説明されているように、各テキスト要素は1つ以上のコードポイントで表されます。コードの完全なリストはここにあります。
各コードポイントは、コンピューターによる内部表現のためにバイナリにエンコードする必要があります。前述のように、それぞれにchar
2バイトが格納されます。以下のコードポイントを1つのU+FFFF
に保存できますchar
。上記のコードポイントU+FFFF
は、2つの文字を使用して単一のコードポイントを表すサロゲートペアとして保存されます。
推測できることがわかったので、テキスト要素は1つchar
として、2つの文字のサロゲートペアとして、またはテキスト要素が複数のコードポイントで表される場合、単一の文字とサロゲートペアのいくつかの組み合わせとして格納できます。それが十分に複雑ではないかのように、一部のテキスト要素は、Unicode Standard Annex#15、UNICODE NORMALIZATION FORMSで説明されているように、コードポイントのさまざまな組み合わせで表すことができます。
間奏
したがって、レンダリングされたときに同じに見える文字列は、実際には異なる文字の組み合わせで構成されている可能性があります。そのような2つの文字列の序数(バイトごと)比較は違いを検出しますが、これは予期しないものであるか望ましくない場合があります。
.Net文字列を再エンコードできます。同じ正規化フォームを使用するようにします。正規化されると、同じテキスト要素を持つ2つの文字列は同じ方法でエンコードされます。これを行うには、string.Normalize関数を使用します。ただし、いくつかの異なるテキスト要素は互いに似ていることに注意してください。:-s
それで、これは質問に関してどのような意味がありますか?テキスト要素'𠈓'
は、単一のコードポイントU + 20213 cjk統一表意文字拡張bで表されます。つまり、1 char
つの文字としてエンコードすることはできず、2つの文字を使用してサロゲートペアとしてエンコードする必要があります。これがstring b
1つchar
長い理由ですstring a
。
確実に(警告を参照)必要があるstring
場合は、a内のテキスト要素の数をカウントする場合は、System.Globalization.StringInfo
このようなクラスを使用する必要があります
。
using System.Globalization;
string a = "abc";
string b = "A𠈓C";
Console.WriteLine("Length a = {0}", new StringInfo(a).LengthInTextElements);
Console.WriteLine("Length b = {0}", new StringInfo(b).LengthInTextElements);
出力を与える、
"Length a = 3"
"Length b = 3"
予想通り。
警告
StringInfo
およびTextElementEnumerator
クラスのUnicodeテキストセグメンテーションの.Net実装は、一般的に有用であり、ほとんどの場合、呼び出し元が期待する応答を生成します。ただし、Unicode Standard Annex#29に記載されているように、「テキストだけでは境界を明確に決定するのに十分な情報が常に含まれているとは限らないため、ユーザーの認識を一致させる目標は常に正確に満たされるとは限りません。」