2

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

现在它适用于所有 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 语句,但我不知道这是否可能。

似乎应该有一个简单的方法来做到这一点,但我无法弄清楚。如果有人可以帮助我,我将不胜感激。

谢谢!

4

5 回答 5

4

这是一个完整的测试,可以根据需要使用正则表达式和 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);
  }
}
于 2012-05-01T02:37:53.663 回答
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); 
 }
}
于 2012-05-01T01:54:10.323 回答
0

第一步是将您的表达式解析为抽象语法树。为此,您可以使用调车场算法

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

于 2012-05-01T01:58:11.040 回答
0

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

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

于 2012-05-01T02:00:05.427 回答
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 方法的顺序。您可能需要调整它以获得更复杂的表达式,但希望这会有所帮助。

于 2012-05-01T02:13:58.380 回答