如何在C#中解析类C语法复合逻辑表达式用于数据表过滤?
在C#中解析带括号的复合逻辑表达式并处理括号校验
要实现类C语法的复合逻辑表达式解析,核心要解决两个问题:括号合法性校验和按优先级拆分原子表达式并构建逻辑关系。下面是具体的实现思路和代码示例:
一、先做括号合法性校验
这是最基础的前置检查,用栈就能轻松实现:
public static bool IsParenthesesValid(string expression) { var stack = new Stack<char>(); foreach (var c in expression) { if (c == '(') { stack.Push(c); } else if (c == ')') { if (stack.Count == 0 || stack.Pop() != '(') { return false; // 出现多余的右括号 } } } return stack.Count == 0; // 左括号未闭合时栈不为空 }
调用这个方法,如果返回false,直接提示用户括号不合法即可。
二、拆分复合表达式为原子表达式并保留逻辑关系
因为逻辑运算符有优先级(&&高于||),再加上括号的嵌套,推荐用递归下降解析法来处理,这种方式直观且容易扩展。
核心思路:
- 定义解析层级:先处理括号包裹的子表达式(递归),再处理
&&连接的表达式,最后处理||连接的表达式。 - 跳过表达式中的空格,避免干扰。
- 识别原子表达式:从当前位置开始,直到遇到逻辑运算符或括号为止,截取出来就是原子表达式(比如
Foo > 0)。
示例解析器框架
public class ExpressionParser { private string _expression; private int _pos; public List<string> AtomicExpressions { get; } = new List<string>(); public (Expression, bool Success) Parse(string expression) { _expression = expression.Replace(" ", ""); // 先去除所有空格简化处理 _pos = 0; AtomicExpressions.Clear(); if (!IsParenthesesValid(_expression)) { return (null, false); } try { var expr = ParseOrExpression(); return (expr, true); } catch { return (null, false); // 其他语法错误,比如运算符位置不对 } } // 处理 || 连接的表达式 private Expression ParseOrExpression() { var left = ParseAndExpression(); while (_pos < _expression.Length && _expression[_pos] == '|' && _expression[_pos+1] == '|') { _pos += 2; // 跳过 || var right = ParseAndExpression(); left = Expression.MakeBinary(ExpressionType.OrElse, left, right); } return left; } // 处理 && 连接的表达式 private Expression ParseAndExpression() { var left = ParsePrimaryExpression(); while (_pos < _expression.Length && _expression[_pos] == '&' && _expression[_pos+1] == '&') { _pos += 2; // 跳过 && var right = ParsePrimaryExpression(); left = Expression.MakeBinary(ExpressionType.AndAlso, left, right); } return left; } // 处理括号和原子表达式 private Expression ParsePrimaryExpression() { if (_pos >= _expression.Length) throw new InvalidOperationException("Unexpected end of expression"); if (_expression[_pos] == '(') { _pos++; // 跳过 ( var expr = ParseOrExpression(); if (_pos >= _expression.Length || _expression[_pos] != ')') throw new InvalidOperationException("Missing closing parenthesis"); _pos++; // 跳过 ) return expr; } else { // 提取原子表达式:直到遇到 &&、||、(、) 为止 int start = _pos; while (_pos < _expression.Length && !IsOperatorOrParenthesis(_expression[_pos])) { _pos++; } var atomicExprStr = _expression.Substring(start, _pos - start).Trim(); AtomicExpressions.Add(atomicExprStr); // 这里调用你已有的原子表达式转Expression的方法 return ConvertAtomicExpressionToExpression(atomicExprStr); } } private bool IsOperatorOrParenthesis(char c) { return c == '&' || c == '|' || c == '(' || c == ')'; } // 替换成你自己的原子表达式转Expression逻辑 private Expression ConvertAtomicExpressionToExpression(string atomicExpr) { // 示例:假设你的数据表是DataTable,这里需要根据列名和操作符构建表达式 // 比如 atomicExpr 是 "Foo > 0",拆分列名、操作符、值,然后构建Expression // 这里只是占位,替换成你已有的实现即可 var param = Expression.Parameter(typeof(DataRow), "row"); // 示例逻辑,实际需要你自己处理不同操作符和类型 var parts = atomicExpr.Split(new[] { '>', '<', '=', '!' }, StringSplitOptions.RemoveEmptyEntries); var columnName = parts[0].Trim(); var valueStr = parts[1].Trim(); var columnExpr = Expression.Property(param, "Item", Expression.Constant(columnName)); var valueExpr = Expression.Constant(int.Parse(valueStr)); return Expression.MakeBinary(ExpressionType.GreaterThan, columnExpr, valueExpr); } }
使用方式
var parser = new ExpressionParser(); var input = "(Foo > 0 && Foo < 20) || Bar == True"; var (expression, success) = parser.Parse(input); if (!success) { Console.WriteLine("表达式格式错误,可能是括号未闭合或语法错误"); return; } // 此时 parser.AtomicExpressions 里就是所有拆分出的原子表达式:["Foo>0", "Foo<20", "Bar==True"] // 可以用构建好的expression来过滤DataTable,比如编译成委托: var filter = Expression.Lambda<Func<DataRow, bool>>(expression, Expression.Parameter(typeof(DataRow), "row")).Compile(); var filteredRows = dataTable.AsEnumerable().Where(filter).ToList();
三、额外注意事项
- 如果需要支持更多运算符(比如
!=、>=、<=),需要在IsOperatorOrParenthesis和原子表达式解析逻辑中补充处理,避免误拆分。 - 原子表达式中的值类型需要做类型转换,比如字符串、布尔值、数值的区分,避免解析错误。
- 可以添加错误定位逻辑,比如记录出错的位置,给用户更明确的提示(比如“第X个字符处括号未闭合”)。
内容的提问来源于stack exchange,提问作者Jake
相关产品推荐
相关产品推荐

