Java 用于验证数学表达式的正则表达式
我试图确定给定的输入是否是有效的数学表达式。这是我目前的代码,但只有当输入是一个整数(例如100、200、5、7)时,它才会返回true 关于我正在努力实现的目标的更多信息: 为简单起见,只假设整数(因此没有变量和小数点)。Java 用于验证数学表达式的正则表达式,java,regex,validation,math,expression,Java,Regex,Validation,Math,Expression,我试图确定给定的输入是否是有效的数学表达式。这是我目前的代码,但只有当输入是一个整数(例如100、200、5、7)时,它才会返回true 关于我正在努力实现的目标的更多信息: 为简单起见,只假设整数(因此没有变量和小数点)。 运算符为:+,-,*,/,%。 仅使用括号(因此不使用括号或大括号) 示例: 有效: 123 1*2(3+4)%7 3--4+5*-7 13(12)+11-(7*15%(11-2)/4) (((((-99999))))) 1+2) )5-- 3+*
运算符为:+,-,*,/,%。
仅使用括号(因此不使用括号或大括号) 示例: 有效:
123
1*2(3+4)%7
3--4+5*-7
13(12)+11-(7*15%(11-2)/4)
(((((-99999)))))
1+2)
)5--
3+*12
)(++**//
(50)+12)
无效
123
1*2(3+4)%7
3--4+5*-7
13(12)+11-(7*15%(11-2)/4)
(((((-99999)))))
1+2)
)5--
3+*12
)(++**//
(50)+12)
此外,如果可能的话,还可以对正则表达式的工作原理进行简单的解释吗?我对这个话题不太熟悉。我在概念上理解它,但在代码中实现它时遇到困难。正如一些评论所说,仅仅使用正则表达式匹配是不可能的。事实上,匹配平衡括号是经典的“简单正则表达式无法解决的问题”之一。只要数学表达式可以包含任意嵌套的括号,就不能用正则表达式验证它 然而,验证一种较小的语言是可能的,然后我们可以通过少量的编码将其构建到您的语言的验证例程中。较小的语言与您的语言一样,但有一个变化:不允许使用括号。然后,语言中的有效表达式如下所示:
INTEGER OP INTEGER OP INTEGER OP .... OP INTEGER
另一种说法是“一个整数
后跟零个或多个OP
整数
序列”。这可以转换为正则表达式,如下所示:
Pattern simpleLang = Pattern.compile("-?\\d+([-+*%/]-?\\d+)*");
所以-?\d+
表示整数
,[-+*%/]
表示OP
。好的,现在我们怎么用这个?首先,让我们修改它,在整数之间添加任意空格,并使模式成为一个静态,因为我们将把这个验证逻辑封装在一个类中:
static Pattern simpleLang = Pattern.compile("\\s*-?\\d+(\\s*[-+*%/]\\s*-?\\d+)*\\s*");
(请注意,我们不允许负号与其后面的数字之间有空格,因此不允许使用3--4
,即使允许使用3--4
)
现在,为了验证完整的语言,我们需要重复地找到一个位于最内层括号级别的块(因此,一个块本身不包含paren,但被一个开-关paren对包围),验证paren中的内容是否与简单语言匹配,然后替换该块(包括周围的paren)使用一些整数,由空格包围,因此它被认为是与周围的东西分开的。所以逻辑是这样的:
INTEGER OP INTEGER OP INTEGER OP .... OP INTEGER
expr
进来的是11-(7*15%(11-2)/4)
- 最里面的括号是
11-2
11-2
与简单语言匹配吗?对!李>
- 用一些整数替换
(11-2)
。例如,使用1
expr
现在是11-(7*15%1/4)
- 最里面的括号是
7*15%1/4
7*15%1/4
与简单语言匹配吗?对!李>
- 用一些整数替换
(7*15%1/4)
。例如,使用1
expr
现在是11-1
- 没有更多的参数,所以问:
expr
与简单语言匹配吗?对!李>
在代码中,它的作用是:
static Pattern simpleLang = Pattern.compile("\\s*-?\\d+(\\s*[-+*%/]\\s*-?\\d+)*\\s*");
static Pattern innerParen = Pattern.compile("[(]([^()]*)[)]");
public static boolean validateExpr(String expr) {
while (expr.contains(")") || expr.contains("(")) {
Matcher m = innerParen.matcher(expr);
if (m.find()) {
if (!simpleLang.matcher(m.group(1)).matches()) {
return false;
}
expr = expr.substring(0,m.start()) + " 1 " + expr.substring(m.end());
} else {
// we have parens but not an innermost paren-free region
// This implies mismatched parens
return false;
}
}
return simpleLang.matcher(expr).matches();
}
请注意,有一个您称为“valid”的表达式不会被称为valid:即表达式13(12)+11-(7*15%(11-2)/4)
。这将被视为无效,因为在13和12之间没有运算符。如果希望允许这种隐式乘法,最简单的方法是在简单语言中添加
(空格字符)作为允许的运算符,因此将simpleLang
更改为:
static Pattern simpleLang = Pattern.compile("\\s*-?\\d+(\\s*[-+ *%/]\\s*-?\\d+)*\\s*");
(Java)正则表达式不能做到这一点。一些正则表达式引擎确实支持类似递归这样的功能,但Java不在其中。为此,如果Java支持上下文无关语法,请尝试寻找它。是这样吗?我(错误地)认为,如果可以构造CFG,那么也可以创建一个可实现的正则表达式。您的主要问题是括号/表达式的无限嵌套:Java中的正则表达式不适合递归,因此您的问题没有答案。您需要一个解析器。如果您放弃搜索正则表达式解决方案,请开始阅读的答案。我想你会发现一些有用的东西。例如,Javascript引擎的计算器。如果它抛出一个ScriptException
表达式无效。是的,可能有更有效的方法来验证这一点(除其他外,我可以在Matcher
上使用replacement
方法),但我的目标是“最容易理解”的效率过高。使用有效的表达式((a+b)+(c*d)),这并不完美,目前它被认为是错误的表达。这个问题非常明确地指出:“为了简单起见,只假设整数(所以没有变量和小数点)”。如果希望将其扩展为包含变量,则需要将simpleLang
更改为除了整数之外还包含这些变量。