Home / ぼやきごと / 2013-03-14 / C#:サロゲートペアを考慮した文字の列挙に foreach を使えるようにする
C#:サロゲートペアを考慮した文字の列挙に foreach を使えるようにする

C#:サロゲートペアを考慮した文字の列挙に foreach を使えるようにする

C#で文字列を1文字ずつ処理する場合、何も考えなければ次のような感じで書くと思います。

すべて開くすべて閉じる
  1
  2
  3
  4
  5
 
 
-
-
!
string text = "あいうえお";
foreach (var c in text)
{
    // c を処理する
}

しかしこれだと文字列にサロゲートペア(4バイトで表されるUnicode文字)が含まれている場合に正常な処理が行えません。
なのでサロゲートペアを考慮した列挙を行う場合は System.Globalization.StringInfo クラスの静的メソッド GetTextElementEnumerator を使って取得した TextElementEnumerator オブジェクトを用いて処理することになります。

すべて開くすべて閉じる
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 
 
 
-
|
|
-
-
!
!
|
-
-
!
!
string text = "あいうえお";
var e = StringInfo.GetTextElementEnumerator(text);
while (e.MoveNext())
{
    string s = e.GetTextElement();
    if (s.Length == 1)
    {
        // 通常文字を処理
        char c = s[0];
    }
    else
    {
        // サロゲートペアを処理
    }
}

しかし私はこのメソッドを使うたびに思うのです。
IEnumerator じゃなくて IEnumerable なオブジェクトを返してくれよ!」と。
なぜなら、そうすれば foreach なり各種拡張メソッドなりが利用できるからです。

そういうわけでこんなクラスを作りました。

すべて開くすべて閉じる
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 
 
 
 
 
-
-
|
|
!
-
-
|
|
|
!
-
|
!
|
-
|
|
!
|
-
|
|
|
!
-
|
!
|
|
|
|
-
|
!
|
|
!
!
using System;
using System.Collections;
using System.Globalization;
 
namespace ruche.util
{
    /// <summary>
    /// 文字列に対する TextElementEnumerator 列挙子を公開するクラス。
    /// </summary>
    public class TextElementEnumerable : IEnumerable
    {
        /// <summary>
        /// コンストラクタ。
        /// </summary>
        /// <param name="source">列挙対象となる文字列。</param>
        public TextElementEnumerable(string source)
        {
            this.Source = source;
        }
 
        /// <summary>
        /// 列挙対象文字列を取得する。
        /// </summary>
        public string Source { get; private set; }
 
        /// <summary>
        /// TextElementEnumerator 列挙子を取得する。
        /// </summary>
        /// <returns>TextElementEnumerator 列挙子。</returns>
        public TextElementEnumerator GetEnumerator()
        {
            return StringInfo.GetTextElementEnumerator(this.Source);
        }
 
        #region IEnumerable の明示的実装
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
 
        #endregion
    }
}

このクラスを使えば次のように記述できます。

すべて開くすべて閉じる
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 
 
-
|
-
-
!
!
|
-
-
!
!
string text = "あいうえお";
foreach (string s in new TextElementEnumerable(text))
{
    if (s.Length == 1)
    {
        // 通常文字を処理
        char c = s[0];
    }
    else
    {
        // サロゲートペアを処理
    }
}

すっきり!

Next: [C#:サロゲートペアを考慮した文字の列挙に拡張メソッドも使えるようにする]
Category: [プログラミング][C#] - 2013-03-14 05:07:34