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); } } } }
方案说明
- 表达式只需要编译一次生成后缀操作序列,后续所有文件行都复用该序列计算,性能很高,完全符合你要的「生成操作序列」的需求
- 天然支持任意层级的括号嵌套,不需要手动处理括号匹配逻辑
- 扩展方便,如果需要加其他运算符、或者修改包含规则为正则匹配,只需要修改对应位置的逻辑即可
运行测试用例会输出匹配行:str5,完全符合你的示例场景要求。
内容的提问来源于stack exchange,提问作者s_herbert
相关产品推荐
相关产品推荐

