如何在Java中验证布尔字符串表达式的语法合法性
验证布尔字符串表达式语法合法性的可行思路
这种布尔表达式的语法验证问题,最靠谱的解法是基于语法规则的结构化分析,而不是简单的字符遍历——毕竟字符遍历很难处理嵌套结构(比如括号)和上下文依赖(比如引号内的空格不需要运算符分隔)。我给你拆解几个关键步骤和可落地的实现思路:
第一步:明确合法的语法规则(上下文无关文法)
首先你得把“什么是合法表达式”用清晰的规则定义出来,比如针对你的需求,合法语法可以这样写(用类似BNF的格式):
<表达式> → <表达式> <二元运算符> <表达式> | <一元运算符> <表达式> | ( <表达式> ) | <合法字符串> <二元运算符> → & | | <一元运算符> → ! <合法字符串> → " [^"]* " // 双引号包裹的任意内容(不含未转义的双引号) | [^&|!()\s]+ // 不含运算符、括号、空格的单个单词
简单说就是:
- 要么是用
&/|连接的两个合法表达式 - 要么是
!后面跟一个合法表达式 - 要么是被括号包裹的合法表达式
- 要么是带引号的短语(允许空格),或者不带引号的单个单词(不能有空格)
第二步:分阶段做基础合法性检查
在做完整语法分析前,可以先做几个低成本的快速检查,提前过滤明显非法的情况:
- 括号匹配检查:用栈实现,遇到
(入栈,遇到)则弹出栈顶(如果栈为空直接非法),遍历结束后栈必须为空。比如你的例子(footballer football !| basket)(最后栈里剩一个(,直接判定非法。 - 引号匹配检查:遍历字符串时记录“是否处于引号内”的状态,遇到未转义的
"就切换状态;遍历结束后必须处于“非引号内”状态,否则就是引号未闭合。 - 无意义运算符组合检查:比如
!|这种,!作为一元运算符后面必须跟表达式,而不是另一个运算符;还有&&、||、&!这类连续运算符,都是非法的。另外要确保表达式不会以二元运算符开头/结尾(比如|football或football&)。
第三步:用递归下降解析器做完整语法验证
因为你的语法规则很简单,递归下降解析器是最容易实现的方案——不需要依赖复杂的语法分析库,自己写几行函数就能搞定。核心思路是把语法规则拆成几个递归函数:
parse_expression():处理最外层的表达式(比如用|连接的多个子表达式)parse_term():处理优先级更高的表达式(比如用&连接的子表达式,如果你的规则里&优先级高于|的话)parse_factor():处理最基础的单元——要么是括号包裹的表达式,要么是!开头的表达式,要么是合法字符串
举个简化的逻辑伪代码:
def parse_expression(s, pos): pos = parse_term(s, pos) while pos < len(s) and s[pos] in ['|']: pos += 1 # 跳过运算符 pos = parse_term(s, pos) return pos def parse_term(s, pos): pos = parse_factor(s, pos) while pos < len(s) and s[pos] in ['&']: pos += 1 pos = parse_factor(s, pos) return pos def parse_factor(s, pos): # 跳过空格 while pos < len(s) and s[pos].isspace(): pos += 1 if pos >= len(s): return -1 # 非法,表达式不完整 if s[pos] == '!': pos += 1 return parse_factor(s, pos) # !后面必须跟一个factor elif s[pos] == '(': pos += 1 pos = parse_expression(s, pos) if pos >= len(s) or s[pos] != ')': return -1 # 括号未闭合 pos += 1 return pos elif s[pos] == '"': pos += 1 # 找到下一个未转义的" while pos < len(s) and s[pos] != '"': if s[pos] == '\\': # 处理转义引号 pos += 1 pos += 1 if pos >= len(s): return -1 # 引号未闭合 pos += 1 return pos else: # 处理不带引号的单个单词 initial_pos = pos while pos < len(s) and s[pos] not in ['&', '|', '!', '(', ')', ' ']: pos += 1 if pos == initial_pos: # 没有匹配到任何字符,非法 return -1 return pos
调用时,只要parse_expression(input_str, 0)返回的位置等于字符串长度,就说明表达式完全合法。
额外注意的细节
- 空格处理:要区分“字符串内的空格”和“表达式之间的分隔空格”——字符串内的空格是合法的,而表达式之间的空格可以忽略,但两个未加引号的单词之间如果只有空格没有运算符,就是非法的(比如你的例子里
footballer football没加引号,中间只有空格,就不符合规则)。 - 转义字符:如果允许字符串内出现双引号(比如
"he said \"hello\""),要记得处理转义逻辑,避免把转义的引号当成字符串结束符。
内容的提问来源于stack exchange,提问作者Yisus
相关产品推荐
相关产品推荐

