动态If语句 - 复杂过滤

时间:2012-05-01 01:20:02

标签: c# if-statement filter

我有一个C#项目,允许用户使用正则表达式创建数据过滤器。他们可以根据需要添加任意数量的过滤器。每个过滤器都包含一个字段和用户键入的正则表达式。

现在它适用于所有AND逻辑。我循环遍历每个过滤器,如果它不匹配,我设置skip = true并退出循环。然后,如果skip == true,我跳过该记录并且不包括它。因此,每个过滤器必须匹配才能包含该字段。

但是,现在他们希望能够添加更复杂的逻辑规则。例如,如果他们创建了4个过滤规则。他们希望能够指定: 1和2和(3或4) 或者他们可能想要指定 1或2或3或4 或者他们可能想要指定 (1和2和3)或4 等等...我认为你明白了。

我添加了一个文本框,可以输入他们想要的逻辑。

我一直在绞尽脑汁,我对如何使这项工作感到难过。我唯一的结论是以某种方式能够创建一个动态的IF语句,该语句基于他们在文本框中键入的文本,但我不知道这是否可能。

似乎应该有一个简单的方法来做到这一点,但我无法弄明白。如果有人能帮助我,我会非常感激。

谢谢!

5 个答案:

答案 0 :(得分: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);
  }
}

答案 1 :(得分: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); 
 }
}

答案 2 :(得分:0)

第一步是将表达式解析为抽象语法树。为此,您可以使用shunting-yard algorithm

完成此操作后,您可以使用虚拟方法或界面递归评估树。例如,您可以拥有一个SimpleNode类,它表示一个简单的表达式(如示例中的1)并且可以对其进行评估。然后你有AndNode代表AND操作,并有两个子节点。它评估子节点并返回是否都成功。

答案 3 :(得分:0)

对规范模式的解释(带有示例代码)应该有所帮助。

http://en.wikipedia.org/wiki/Specification_pattern#C.23

答案 4 :(得分:0)

可能有图书馆为你做这类事情,但在过去,我根据使用Predicate手动推出了这些内容;         使用系统;         使用System.Collections.Generic;         使用System.Linq;         使用System.Text;         使用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方法的顺序。您可能需要针对更复杂的表达式进行调整,但希望这会有所帮助。