Java 用于验证数学表达式的正则表达式

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+*

我试图确定给定的输入是否是有效的数学表达式。这是我目前的代码,但只有当输入是一个整数(例如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+*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
更改为除了整数之外还包含这些变量。