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

Java解析带嵌套括号的自定义布尔表达式以筛选匹配文件行

实现方案

你要做的这个需求本质是自定义布尔表达式的解析与动态求值,用调度场算法+后缀表达式求值就能完美解决,完全不需要自己递归处理括号,步骤如下:

前置准备:定义规则

  • 运算符优先级:not(一元运算符) > and > or
  • 变量格式:固定为t+数字,对应TextField的输入
  • 语义约定:变量值为true代表当前行包含该变量对应的字符串

完整实现代码

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BooleanExpressionFilter {
    // 运算符优先级配置
    private static final Map<String, Integer> OP_PRIORITY = new HashMap<>();
    static {
        OP_PRIORITY.put("or", 1);
        OP_PRIORITY.put("and", 2);
        OP_PRIORITY.put("not", 3);
        OP_PRIORITY.put("(", 0);
    }

    // 分词正则:匹配变量t+数字、运算符、括号
    private static final Pattern TOKEN_PATTERN = Pattern.compile("t\\d+|not|and|or|\\(|\\)");

    // 变量映射:key是t1/t2...,value是用户输入的对应字符串
    private Map<String, String> varMapping;
    // 预编译好的后缀操作序列
    private List<String> rpnExpression;

    public BooleanExpressionFilter(Map<String, String> varMapping, String rawExpression) {
        this.varMapping = varMapping;
        // 第一步:表达式分词
        List<String> tokens = tokenize(rawExpression);
        // 第二步:中缀转后缀逆波兰表达式
        this.rpnExpression = infixToRpn(tokens);
    }

    // 表达式分词,拆分出所有合法token
    private List<String> tokenize(String expression) {
        List<String> tokens = new ArrayList<>();
        Matcher matcher = TOKEN_PATTERN.matcher(expression);
        while (matcher.find()) {
            tokens.add(matcher.group());
        }
        return tokens;
    }

    // 调度场算法:中缀表达式转后缀逆波兰表达式
    private List<String> infixToRpn(List<String> infixTokens) {
        List<String> rpn = new ArrayList<>();
        Stack<String> opStack = new Stack<>();

        for (String token : infixTokens) {
            // 变量直接加入后缀列表
            if (token.startsWith("t")) {
                rpn.add(token);
            }
            // 左括号直接压栈
            else if (token.equals("(")) {
                opStack.push(token);
            }
            // 右括号:弹出栈中运算符直到遇到左括号
            else if (token.equals(")")) {
                while (!opStack.peek().equals("(")) {
                    rpn.add(opStack.pop());
                }
                opStack.pop(); // 弹出左括号,不加入后缀序列
            }
            // 普通运算符:按优先级处理
            else {
                while (!opStack.isEmpty() && OP_PRIORITY.get(opStack.peek()) >= OP_PRIORITY.get(token)) {
                    rpn.add(opStack.pop());
                }
                opStack.push(token);
            }
        }
        // 栈中剩余运算符全部加入后缀序列
        while (!opStack.isEmpty()) {
            rpn.add(opStack.pop());
        }
        return rpn;
    }

    // 校验单行文本是否匹配表达式
    public boolean isMatch(String line) {
        Stack<Boolean> stack = new Stack<>();
        for (String token : rpnExpression) {
            switch (token) {
                case "not":
                    stack.push(!stack.pop());
                    break;
                case "and":
                    Boolean b1 = stack.pop();
                    Boolean b2 = stack.pop();
                    stack.push(b2 && b1);
                    break;
                case "or":
                    Boolean b3 = stack.pop();
                    Boolean b4 = stack.pop();
                    stack.push(b4 || b3);
                    break;
                // 变量:判断行是否包含对应字符串
                default:
                    String target = varMapping.get(token);
                    stack.push(line.contains(target));
                    break;
            }
        }
        return stack.pop();
    }

    // 测试用例
    public static void main(String[] args) {
        // 模拟用户TextField输入
        Map<String, String> varMap = new HashMap<>();
        varMap.put("t1", "str1");
        varMap.put("t2", "str2");
        varMap.put("t3", "str3");
        varMap.put("t4", "str4");
        varMap.put("t5", "str5");

        // 模拟用户输入的布尔表达式
        String expression = "not ((t1 and not t3) or (t4 and t2)) or t5";
        BooleanExpressionFilter filter = new BooleanExpressionFilter(varMap, expression);

        // 模拟文件行
        List<String> lines = Arrays.asList("str5", "str4 str2");
        // 过滤输出
        for (String line : lines) {
            if (filter.isMatch(line)) {
                System.out.println("匹配行:" + line);
            }
        }
    }
}

方案说明

  1. 表达式只需要编译一次生成后缀操作序列,后续所有文件行都复用该序列计算,性能很高,完全符合你要的「生成操作序列」的需求
  2. 天然支持任意层级的括号嵌套,不需要手动处理括号匹配逻辑
  3. 扩展方便,如果需要加其他运算符、或者修改包含规则为正则匹配,只需要修改对应位置的逻辑即可
    运行测试用例会输出匹配行:str5,完全符合你的示例场景要求。

内容的提问来源于stack exchange,提问作者s_herbert

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 17:27:02