「キャラクター」はややあいまいな用語で、異なる文脈では異なるものを意味します。あなたの例と同じ結果が欲しいと思います[NSString length]
.
NSString
ドキュメントはこれについて正確に述べているわけではありませんが、文字列内のUTF-16 コード単位[NSString length]
の数を数えています。したがって、U+0000..U+FFFF はそれぞれ 1 つとしてカウントされますが、U+10000..U+10FFFF はそれぞれ 2 つとしてカウントされます。また、サロゲート ペアを分割しないでください。
各 UTF-8 文字の先頭バイトに基づいて、UTF-16 コード ポイントの数をカウントできます。末尾のバイトはばらばらな値のセットを使用するため、文字列内の位置を除いて、状態を追跡する必要はまったくありません(良いニュース: 有限状態マシンはやり過ぎです)。
static const unsigned char BYTE_WIDTHS[256] = {
// 1-byte: 0xxxxxxx
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
// Trailing: 10xxxxxx
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
// 2-byte leading: 110xxxxx
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
// 3-byte leading: 1110xxxx
// 4-byte leading: 11110xxx
// invalid: 11111xxx
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0
};
size_t utf8_utf16width(const unsigned char *string, size_t len)
{
size_t i, utf16len = 0;
for (i = 0; i < len; i++)
utf16len += BYTE_WIDTHS[string[i]];
return utf16len;
}
表は、1 バイト、2 バイト、および 3 バイトの UTF-8 先頭文字の場合は 1、4 バイト UTF-8 の先頭文字の場合は 2 です。これらは に変換すると 2 文字になるためNSString
です。
Haskellでテーブルを生成しました:
elems $ listArray (0,256) (repeat 0) //
[(n,1) | n <- ([0x00..0x7f] ++ [0xc0..0xdf] ++ [0xe0..0xef])] //
[(n,2) | n <- [0xf0..0xf7]]