如何通过Java正则split实现支持value含括号的搜索条件解析
问题说明
当前基于StringTokenizer实现的搜索条件解析逻辑,以()作为全局分隔符拆分token,仅支持value中不含括号的场景,支持的基础查询格式为:
(key:operator:value)(key:operator:value) and (key:operator:value)
原有实现代码如下:
Stack<Object> stack = new Stack<>(); StringTokenizer tokenizer = new StringTokenizer(filterString, "()", true); while (tokenizer.hasMoreElements()) { String token = tokenizer.nextToken(); if (isOpenBrace(token)) { stack.push(token); } else if (isCloseBrace(token)) { Object preVal = null; Criteria criteria = null; String operator = null; while (!isOpenBrace(stack.peek())) { Object top = stack.pop(); if (isString(top)) { String op = (String) top; if (isAnd(op) || isOr(op)) { operator = (String) top; } else { throw new Exception("Invalid operand"); } } else if (isCriteria(top)) { if (preVal != null) { if (isAnd(operator)) { top = ((Criteria) top).and((Criteria) preVal); criteria = (Criteria) top; preVal = top; } else if (isOr(operator)) { top = ((Criteria) top).or((Criteria) preVal); criteria = (Criteria) top; preVal = top; } } else { preVal = top; criteria = (Criteria) preVal; } } } if (criteria != null) { stack.pop(); stack.push(criteria); } } else if (isAnd(token) || isOr(token)) { stack.push(token); } else { String[] parts = token.split(COLON); // do rest stuffs } } if (stack.size() != 1) { throw new Exception("Invalid filter"); }
说明:
Criteria为项目内部自定义类。
需要改造分词逻辑,改用split()搭配正则实现,要求仅将作为条件块边界的括号拆分为独立token,value内部的括号保留在内容中:例如输入(key:operator:va(lue)时,分词结果为"(", "key:operator:va(lue)", ")"。
实现方案
核心思路是仅识别作为语法边界的括号:语法边界的括号有明确的上下文特征,不会和value内部的括号混淆:
- 语法左括号
(:要么是字符串首字符,要么紧接在and/or逻辑符之后 - 语法右括号
):要么是字符串尾字符,要么紧接在and/or逻辑符之前
具体实现步骤:
- 先对输入字符串做空白归一化,统一逻辑符前后的空格格式,避免多空格、制表符干扰匹配
- 用正则零宽断言定位所有语法括号的位置,在语法括号前后插入特殊分隔符(选用业务文本不会出现的
\0空字符即可) - 按特殊分隔符拆分字符串,过滤空值、去除每个token前后空白,就得到符合要求的token序列
代码实现
// 输入示例 String filterString = "(key:operator:va(lue)"; // 1. 空白归一化:统一逻辑符前后为单个空格,去除首尾空白 String normalized = filterString.trim() .replaceAll("\\s+and\\s+", " and ") .replaceAll("\\s+or\\s+", " or "); // 2. 给语法括号前后加分隔符 // 匹配规则: // 语法左括号:位于字符串开头 或 空格+and/空格+or 之后 // 语法右括号:位于字符串结尾 或 空格+and/空格+or 之前 String withDelimiter = normalized .replaceAll("(^|(?<= and )|(?<= or ))\\(", "\0(\0") .replaceAll("\\)(?=$|(?= and )|(?= or ))", "\0)\0"); // 3. 拆分并过滤空token List<String> tokens = Arrays.stream(withDelimiter.split("\0")) .map(String::trim) .filter(s -> !s.isEmpty()) .collect(Collectors.toList()); // 测试输出:[(, key:operator:va(lue), )],完全符合预期 System.out.println(tokens);
拿到token列表后,直接替换原来的StringTokenizer遍历逻辑即可,原有栈解析的代码不需要做任何修改,就可以自动支持value中包含任意括号的场景。
注意:如果业务场景下value内部可能出现前后带空格的
and/or子串,需要额外增加转义规则(比如用引号包裹value、转义字符标记内容中的逻辑符),避免被误识别为语法边界。
内容的提问来源于stack exchange,提问作者BuggyBeing
相关产品推荐
相关产品推荐

