You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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,直接提示用户括号不合法即可。

二、拆分复合表达式为原子表达式并保留逻辑关系

因为逻辑运算符有优先级(&&高于||),再加上括号的嵌套,推荐用递归下降解析法来处理,这种方式直观且容易扩展。

核心思路:

  1. 定义解析层级:先处理括号包裹的子表达式(递归),再处理&&连接的表达式,最后处理||连接的表达式。
  2. 跳过表达式中的空格,避免干扰。
  3. 识别原子表达式:从当前位置开始,直到遇到逻辑运算符或括号为止,截取出来就是原子表达式(比如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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 03:33:35