[C#]Stringに正規表現の拡張メソッドを追加してLINQで使う

list このエントリーをはてなブックマークに追加

System::String に正規表現が使えたらいいなぁと思いつつ、書いてみたのがこれです。

■用法

        [TestMethod]
        public void TestMethod1()
        {
            string[] lst = {
                "masuda",
                "tomoaki",
                "yamasaki",
                "yumi" };

            var q = from t in lst
                    where t.IsMatch("y.*i")
                    select t;

            Assert.AreEqual(2, q.Count());
            Assert.AreEqual("yamasaki", q.First());
        }

        [TestMethod]
        public void TestMethod2()
        {
            string[] lst = {
                "masuda",
                "tomoaki",
                "yamasaki",
                "yumi" };

            var q = from t in lst
                    where t.IsMatch("ki$")
                    select new { match = t.Match("..ki$") };

            Assert.AreEqual(2, q.Count());
            Assert.AreEqual("oaki", q.First().match);
        }

単純に、Regex の Match, IsMatch メソッドを String クラスにくっつけただけなのですが、LINQ 内で直接使えるというのがメリットですね。別途、new Regex(…) を使えば、LINQ 内でも書けるのすが、それだと文が長くなってしまうしというパターンです。

■実装

実装はおそろしく簡単で以下な感じ。単純に Match, IsMatch をラップします。

using System.Text.RegularExpressions;

namespace Moonmile.StringRegex
{
    public static class StringRegexExtentions
    {
        public static string Match( this string target, string pattern )
        {
            var rx = new Regex(pattern);
            var mc = rx.Matches(target);
            return (mc.Count == 0) ? "" : mc[0].Value;
        }
        public static bool IsMatch(this string target, string pattern)
        {
            var rx = new Regex(pattern);
            return rx.IsMatch( target );
        }
    }
}

■利用方法

で、最終的には何をしたいのかという、以下な感じで2つのファイルの DIFF を調べたかったのです。
2つのファイルに、ファイル名の羅列があって、それを使って同期をするツールですね。マッチさせたい拡張子が限られているのでそれを内部で指定するという現場の即したツールです。

static void Main(string[] args)
{
#if false
	if (args.Length != 2)
	{
	    Console.WriteLine("usage: difffolders [src] [dest]");
	    return;
	}
	var lsrc = new List();
	var ldst = new List();

	StreamReader sr = File.OpenText(src);
	while (!sr.EndOfStream)
	    lsrc.Add(sr.ReadLine());
	sr.Close();
	sr = File.OpenText(dest);
	while (!sr.EndOfStream)
	    ldst.Add(sr.ReadLine());
	sr.Close();

	var llsrc = new List();
	var lldst = new List();

	string[] exts = {
	                    "\\.cpp", "\\.h", "\\.hpp", "\\.rc", "\\.h", "\\.f90", "\\.fi", "\\.for", "\\.inc", "\\.cmn"
	                    };
	foreach (var ext in exts)
	{
	    string ext1 = ext + "$";
	    llsrc.AddRange(lsrc.Where(n => n.IsMatch(ext1) == true).Select(n => n.Replace("C:\\Develop\\Main-branch-0","")));
	    lldst.AddRange(ldst.Where(n => n.IsMatch(ext1) == true).Select(n => n.Replace("C:\\Develop\\Main-branch-1","")));
	}
	llsrc.RemoveAll(n => n.IsMatch("\\Debug")); ;
	lldst.RemoveAll(n => n.IsMatch("\\Debug"));
	llsrc.Sort();
	lldst.Sort();

	// 差分を計算する
	Debug.Print("src count: {0}", llsrc.Count);
	Debug.Print("dst count: {0}", lldst.Count);

	var onlysrc = llsrc.Where(n => !lldst.Contains(n)).Select(n => n );
	var onlydst = lldst.Where(n => !llsrc.Contains(n)).Select(n => n );
	var andlst = llsrc.Where(n => lldst.Contains(n)).Select(n => n);

	Debug.Print("only/and {0} {1} {2}", onlysrc.Count(), onlydst.Count(), andlst.Count());

	StreamWriter sw = new StreamWriter(File.OpenWrite("out-diff.txt"));
	foreach( var f in onlysrc )
	    sw.WriteLine("- {0}", f );
	foreach (var f in onlydst)
	    sw.WriteLine("+ {0}", f);
	foreach (var f in andlst)
	    sw.WriteLine("m {0}", f);
	sw.Close();
}

で、これを作るうえで困ったのが、.NET の正規表現のまずさ(Regexの実装かな?)ですね。本来ならば、

var q = llsrc.Where( n => n.IsMatch("\\.(cpp|h|hpp|rc|f98|for|fi|inc|cmn)").Select( n => n );

のように一発で書きたいところなのですが、.NET 正規表現では、OR 演算子が無い模様。Group を使うんですかね?

2012/09/18 追記

とか思ったら、

代替構成体
http://msdn.microsoft.com/ja-jp/library/36xybswe(v=vs.100).aspx

に「|」が使えるやんッ!!! ってことで、多分先のコードは書けるハズ。後で書き直しますか。

 

 

 

正規表現クラス
http://msdn.microsoft.com/ja-jp/library/30wbz966(v=vs.80)

仕方がないので array で用意して foreach で廻しています。Perl 互換の正規表現ライブラリがあればそれを使いたいなと。

ちなみに、ファイル名から拡張子を取る Path.GetExtension を使っても良いんですけどね。

	string[] exts = {
            ".cpp", ".h", ".hpp", ".rc", ".h", ".f90", ".fi", ".for", ".inc", ".cmn"
            };

	foreach ( var n in lsrc ) {
		if (exts.Contains(Path.GetExtension(n)))
		{
			llsrc.Add(n.Replace("C:\\Develop\\Main-branch-0", ""));
		}
	}
	foreach ( var n in ldst ) {
		if (exts.Contains(Path.GetExtension(n)))
		{
			lldst.Add(n.Replace("C:\\Develop\\Main-branch-1", ""));
		}
	}
カテゴリー: C# パーマリンク

[C#]Stringに正規表現の拡張メソッドを追加してLINQで使う への3件のフィードバック

  1. ピンバック: .NET Clips

コメントは停止中です。