簡易文字列置換
- 2009-08-23
static void Main(string[] args)
{
// {}で括った部分を(ラムダ式/匿名型)を使って置換する
var input = "食べ物は{tabemono}で飲み物は{nomimono}";
var r1 = input.Replace(new { tabemono = "たこ焼き", nomimono = "コーラ" });
var r2 = input.Replace(tabemono => "たこ焼き", nomimono => "コーラ");
}
前も書いたというかJavaScriptでやりましたが、それのC#移植。簡易テンプレート的な文字列置換。ラムダ式版と匿名型版の二つでやってみました。ラムダ式だと、見た目が少し短くて何となく格好良いのですが、変数名の入力時にIntelliSenseが動いてしまうので結構鬱陶しかったり。匿名型のほうはObjectを受け取る関数なので、危なっかしいのが嫌ですね……。
static class Extensions
{
// 正規表現の|でキーを連結して辞書から置換
private static string Replace(string input, Dictionary<string, string> dict)
{
var pattern = string.Format("{{({0})}}", dict.Select(kvp => Regex.Escape(kvp.Key)).ToJoinedString("|"));
return Regex.Replace(input, pattern, m => dict[m.Groups[1].Value]);
}
/// <param name="anonymousType">{pattern = "replacement"}</param>
public static string Replace(this string input, Object anonymousType)
{
var dict = anonymousType.GetType()
.GetProperties()
.ToDictionary(pi => pi.Name, pi => pi.GetValue(anonymousType, null).ToString());
return Replace(input, dict);
}
/// <param name="exprs">pattern => "replacement"</param>
public static string Replace(this string input, params Expression<Func<Object, string>>[] exprs)
{
var dict = exprs.ToDictionary(e => e.Parameters[0].Name, e => e.Compile().Invoke(null));
return Replace(input, dict);
}
// 文字列連結補助メソッド(これないとシンドイので)
public static string ToJoinedString<T>(this IEnumerable<T> source, string separator)
{
var index = 0;
return source.Aggregate(new StringBuilder(),
(sb, o) => (index++ == 0) ? sb.Append(o) : sb.AppendFormat("{0}{1}", separator, o))
.ToString();
}
}
速度は、匿名型版のほうが圧倒的に速いです。100000回の繰り返しが匿名型だと2秒なのに対してラムダ式版は30秒かかった。Compile().Invokeがアレなんですかねえ。ちなみに、string.Formatでふつーにやった場合は0.03秒でした。まー、でも、メール雛型の置換に、とかちょっとしたことにそこそこ便利に使えるかなー、とは思いつつもっと効率考えてちゃんと組んだ方がいい気もする。
ラムダ式から取り出すのはC# 3.0 における疑似 Map 生成リテラル - NyaRuRuの日記から、匿名型はprototype.jsとかPHPのstrtrとか色々。あとややニッチな Anonymous Types の使い方をまとめてみる (C# 3.0) - NyaRuRuの日記です。