2014年8月22日

4Byte 文字を含んだ文字列の文字列挙

普通、文字列を foreach で回すだけで文字列挙は可能だが、4Byte 文字が混じっていると話が違ってくる。
4Byte 文字はその名のとおり 4Byte なので char 型 2 個分に相当する。
※char 型 は 2Byte、.NET の内部文字コードは UTF-16

[文字列挙サンプル]

static void Main()
{
    var text = "鮭𩸽"; // さけ(2Byte) ほっけ(4Byte)
    Console.WriteLine(text.Length);
    foreach( var c in text ) Console.WriteLine(c);
}
[結果]
3
鮭
�
�
※UTF-8 コードページによる表示なので、実行環境は PowerShell ISE 推奨

StringInfo

では、4Byte 文字も 1 文字として扱いたい場合なのだが、StringInfo が用意されている。
列挙に関しては、StringInfo.GetTextElementEnumerator があるが、TextElementEnumerator を返すため、少々使い勝手が悪い。
そこで、次の構造体(とおまけの拡張メソッド)を作成。

public struct TextElementEnumerable : IEnumerable
{
    public string Source;

    public TextElementEnumerator GetEnumerator()
    {
        return StringInfo.GetTextElementEnumerator(Source);
    }
    IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }

    public static TextElementEnumerable Create(string source)
    {
        return new TextElementEnumerable() { Source = source };
    }
}

// TextElementEnumerator が IEnumerator しか実装していないため、
// IEnumerable<string> が欲しい場合はキャストが必要
// ということを考慮した拡張メソッド
public static class TextExtensions
{
    public static IEnumerable<string> GetTextElementEnumerable(this string source)
    {
        if( source == null ) throw new ArgumentNullException("source");
        return TextElementEnumerable.Create(source).Cast<string>();
    }
}

[使用例]

static void Main()
{
    var text = "鮭𩸽"; // さけ(2Byte) ほっけ(4Byte)
    // 4Byte 文字を考慮した文字数
    Console.WriteLine(new StringInfo(text).LengthInTextElements);
    // 4Byte 文字を考慮した文字列挙
    foreach( var c in text.GetTextElementEnumerable() ) Console.WriteLine(c);
}
[結果]
2
鮭
𩸽

参考URL

0 件のコメント:

コメントを投稿