2012-05-01 14 views
2

私は、ユーザーが正規表現を使用してデータにフィルタを作成できるようにするC#プロジェクトを用意しています。彼らは彼らが望むだけ多くのフィルターを追加することができます。各フィルタは、フィールドとユーザーが入力する正規表現で構成されます。Dynamic Ifステートメント - 複合フィルタリング

今はすべてのANDロジックで動作します。私は各フィルターをループし、一致しない場合はskip = trueを設定してループから抜け出します。 skip == trueならば、私はそのレコードをスキップし、それをインクルードしません。フィールドを含めるには、各フィルタが一致する必要があります。

しかし、今や彼らはより複雑なロジックルールを追加する必要があります。たとえば、4つのフィルタルールが作成されている場合などです。彼らは 1 AND 2 AND(3 OR 4) のいずれかを指定するか、 1 OR 2 OR 3 OR 4 を指定するか、 (1 AND 2 AND 3)OR 4 など...私はあなたがポイントを得ると思います。

私は彼らが望むロジックで入力できるテキストボックスを追加しました。

私は頭を悩ませています。私はこの仕事をどうやって作るのかについて困惑しています。私の唯一の結論は、何らかの形でテキストボックスに入力したテキストに基づく動的IF文を何らかの形で作成できることですが、それが可能なのかどうかわかりません。

これを行うには簡単な方法があるようですが、わかりません。誰かが私を助けることができたら、本当に感謝します。

ありがとうございます!次のような

+0

この種の問題には、動的表現ツリーを使用できる場合があります。 http://msdn.microsoft.com/en-us/library/bb882637.aspx –

+0

これらは常にこの順序で使用されますか1234 –

+0

いいえ、任意の順序で指定できます。したがって(1または3)と(2または4)が有効です。 – jkruer01

答えて

2

ここでは、正規表現とAND、OR、および大括弧で必要なすべてのテストを行います。これは演算子ANDORと括弧()しかサポートしておらず、入力がいくらか整形されることを期待しています(正規表現にはスペースを入れてはいけません)。解析は改善され、アイデアは変わりません。ここで

は、全体的なテストである:ここでは

var input = ".* AND [0-9]+ AND abc OR (abc AND def)"; 
var rpn = ParseRPN(input); 
var test = GetExpression(new Queue<string>(rpn.Reverse())).Compile(); 
test("abc"); // false 
test("abc0"); // true 
test("abcdef"); // true 

はポーランド記法を逆にする構文解析です:

public Queue<string> ParseRPN(string input) 
{ 
    // improve the parsing into tokens here 
    var output = new Queue<string>(); 
    var ops = new Stack<string>(); 
    input = input.Replace("(","(").Replace(")",")"); 
    var split = input.Split(' '); 

    foreach (var token in split) 
    { 
     if (token == "AND" || token == "OR") 
     { 
      while (ops.Count > 0 && (ops.Peek() == "AND" || ops.Peek() == "OR")) 
      { 
       output.Enqueue(ops.Pop()); 
      } 
      ops.Push(token); 
     } 
     else if (token == "(") ops.Push(token); 
     else if (token == ")") 
     { 
      while (ops.Count > 0 && ops.Peek() != "(") 
      { 
       output.Enqueue(ops.Pop()); 
      } 
      ops.Pop(); 
     } 
     else output.Enqueue(token); // it's a number   
    } 

    while (ops.Count > 0) 
    { 
     output.Enqueue(ops.Pop()); 
    } 

    return output; 
} 

と魔法GetExpression

public Expression<Func<string,bool>> GetExpression(Queue<string> input) 
{ 
    var exp = input.Dequeue(); 
    if (exp == "AND") return GetExpression(input).And(GetExpression(input)); 
    else if (exp == "OR") return GetExpression(input).Or(GetExpression(input)); 
    else return (test => Regex.IsMatch(test, exp)); 
} 

注これは頼りありませんPredicateBuilderが使用されていますが、ここで使用されている拡張機能は完全です。

public static class PredicateBuilder 
{ 
    public static Expression<Func<T, bool>> True<T>() { return f => true; } 
    public static Expression<Func<T, bool>> False<T>() { return f => false; } 

    public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr1, 
                 Expression<Func<T, bool>> expr2) 
    { 
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression>()); 
    return Expression.Lambda<Func<T, bool>> 
      (Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters); 
    } 

    public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1, 
                 Expression<Func<T, bool>> expr2) 
    { 
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression>()); 
    return Expression.Lambda<Func<T, bool>> 
      (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters); 
    } 
} 
+0

それはうまくいった!ご協力いただきありがとうございます! – jkruer01

1

何か - バイナリの動作を表しており、あなたのツリーを構築するために操作クラスを定義します。

interface IFilter 
{ 
bool Filter(Record r); 
} 

class SimpleFilter : IFilter 
{ 
bool Filter(Record r) 
{ 
    return RegExpMatch(r); 
} 
} 

class AndFilter : IFilter 
{ 
public AndFilter(IFilter left, IFilter right) {} 

bool Filter(Record r) 
{ 
    return left.Filter(r) && right.Filter(r); 
} 
} 

class OrFilter : IFilter 
{ 
public OrFilter(IFilter left, IFilter right) {} 

bool Filter(Record r) 
{ 
    return left.Filter(r) || right.Filter(r); 
} 
} 
0

最初のステップは、抽象構文木にあなたの式を解析することです。これを行うには、shunting-yard algorithmを使用します。

これを実行すると、仮想メソッドまたはインターフェイスを使用して、ツリーを再帰的に評価できます。たとえば、クラスを持つことができます。これは単純な式(例では1など)を表し、評価することができます。次に、AND操作を表すAndNodeがあり、2つの子ノードがあります。子ノードを評価し、両方が成功したかどうかを返します。

0

はあなたのためにこの種のものを行うためのライブラリがあるかもしれませんが、過去に、私は述語を使用して周りに基づくこれらの線に沿って手巻きの何かを、しました。 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions;

namespace ConsoleApplication1 
    { 
     public enum CombineOptions 
     { 
      And, 
      Or, 
     } 

     public class FilterExpression 
     { 
      public string Filter { get; set; } 
      public CombineOptions Options { get; private set; } 
      public FilterExpression(string filter, CombineOptions options) 
      { 
       this.Filter = filter; 
       this.Options = options; 
      } 
     } 

     public static class PredicateExtensions 
     { 
      public static Predicate<T> And<T> 
       (this Predicate<T> original, Predicate<T> newPredicate) 
      { 
       return t => original(t) && newPredicate(t); 
      } 

      public static Predicate<T> Or<T> 
       (this Predicate<T> original, Predicate<T> newPredicate) 
      { 
       return t => original(t) || newPredicate(t); 
      } 
     } 

     public static class ExpressionBuilder 
     { 
      public static Predicate<string> BuildExpression(IEnumerable<FilterExpression> filterExpressions) 
      { 
       Predicate<string> predicate = (delegate 
       { 
        return true; 
       }); 


       foreach (FilterExpression expression in filterExpressions) 
       { 
        string nextFilter = expression.Filter; 
        Predicate<string> nextPredicate = (o => Regex.Match(o, nextFilter).Success); 

        switch (expression.Options) 
        { 
         case CombineOptions.And: 
          predicate = predicate.And(nextPredicate); 
          break; 
         case CombineOptions.Or: 
          predicate = predicate.Or(nextPredicate); 
          break; 
        } 
       } 

       return predicate; 
      } 
     } 

     class Program 
     { 
      static void Main(string[] args) 
      { 
       FilterExpression f1 = new FilterExpression(@"data([A-Za-z0-9\-]+)$", CombineOptions.And); 
       FilterExpression f2 = new FilterExpression(@"otherdata([A-Za-z0-9\-]+)$", CombineOptions.And); 
       FilterExpression f3 = new FilterExpression(@"otherdata([A-Za-z0-9\-]+)$", CombineOptions.Or); 

       // result will be false as "data1" does not match both filters 
       Predicate<string> pred2 = ExpressionBuilder.BuildExpression(new[] { f1, f2 }); 
       bool result = pred2.Invoke("data1"); 

       // result will be true as "data1" matches 1 of the 2 Or'd filters 
       Predicate<string> pred3 = ExpressionBuilder.BuildExpression(new[] { f1, f3 }); 
       result = pred3.Invoke("data1"); 
      } 
     } 
    } 

あなたが今行う必要がありますすべてはあなたのFilterExpressionsがBuildExpression方法に送信する順序を決定する「ブラケット」を解析しています。より複雑な表現のためにそれを微調整する必要があるかもしれませんが、うまくいけば、これは役に立ちます。

関連する問題