Java确定字符串是否与string.matches匹配所用的时间过长

Java确定字符串是否与string.matches匹配所用的时间过长,java,regex,Java,Regex,我有下面的正则表达式,它匹配开始处以括号中文本结尾的任何字符“Hi(Stackoverflow)” 当我输入要匹配的文本时,程序就一直在运行 String pattern = "^[a-zA-Z]+([\\s]*[\\w]*)*\\([\\w]+\\)" String text = "Asdadasdasd sadsdsad sdasd (s)" String text2 = "Asdadasdasd sadsdsad sdasd (s) sdsd" System.out.println(te

我有下面的正则表达式,它匹配开始处以括号中文本结尾的任何字符
“Hi(Stackoverflow)”

当我输入要匹配的文本时,程序就一直在运行

String pattern = "^[a-zA-Z]+([\\s]*[\\w]*)*\\([\\w]+\\)"
String text = "Asdadasdasd sadsdsad sdasd (s)"
String text2 = "Asdadasdasd sadsdsad sdasd (s) sdsd"

System.out.println(text.matches(pattern)) - it works
System.out.println(text2.matches(pattern)) - never ending story
怎么了

private static final Pattern pattern = Pattern.compile("[a-zA-Z]+([\\s]*[\\w]*)*\\([\\w]+\\)");

public static void main(String[] args) {

    String text = "Asdadasdasd sadsdsad sdasd (s)";
    String text2 = "Asdadasdasd sadsdsad sdasd (s) sdsd (k) ssdd";

    match(text);
    match(text2);
}


private static void match(String text) {
    Matcher matcher = pattern.matcher(text);
    while (matcher.find()) {
        System.out.println(matcher.group(0));
    }
}
输出为:

Asdadasdasd sadsdsad sdasd (s)
Asdadasdasd sadsdsad sdasd (s)
sdsd (k)
由于正则表达式中的
*
,第二个需要很长时间(或者至少可能需要很长时间,具体取决于实现)

您的正则表达式开始尝试如下匹配:

[a-zA-Z]+\s*\w*\s*\w*\s*\w*\(\w+\)[不匹配]
asdadasdasdasdsad sdasd X(s)sdsd
在这一点上,你可能希望它说“好吧,不匹配,我们完成了”

但事实并非如此

相反,它将试图找到一个匹配的工作(因为它不是所有的计算机容易找出,回溯将是浪费时间在这种情况下)

当它以前将第二个
\w*
匹配到
sdasd
时,它现在将尝试少一个字符,即
sdas
,然后它将添加另一个
\s*\w*
,它将为
\s*
匹配0个字符,为
\w*
匹配
d

[a-zA-Z]+\s*\w*\s*\w*\s*\w*\s*\w*\(\w+\)[不匹配]
asdadasdasdasd sadsdas X d X(s)sdsd
这也不起作用,因此它将尝试使用
sda
,然后再尝试
sd
,这将不起作用,并导致它将其进一步分解为
sda
s
d

[a-zA-Z]+\s*\w*\s*\w*\s*\w*\s*\w*\(\w+\)[不匹配]
asdadasdasdasdsad sda X sd X(s)sdsd
[a-zA-Z]+\s*\w*\s*\w*\s*\w*\s*\w*\s*\w*\(\w+\)[不匹配]
asdadasdasdasd sadsdad sda X s X d X(s)sdsd
依此类推,直到每个
\w
只匹配一个字符

PS:上面的内容并不一定就是它所做的,它更倾向于给出一个发生了什么的基本概念

PPS:为了简洁起见,使用了
\
而不是
\\

你怎么修理它? 有几种方法可以解决这个问题

需要最少更改的可能是使用
(\\s*\\w*)*+
而不是-
*+
生成
*
,这可以防止它完全回溯(这与我们在这里想要的一致)

^[a-zA-Z]+(\\s*\\w*)*+\(\\w+\\)
同样有效的方法是使用
\\s+
而不是
\\s*
,尽管这会导致一些稍微不同的行为(特别是0-9不能再出现在第一个空格之前,可以通过在括号之前添加
\\w*
来修复)

这修复了它,因为我们无法再为
\\s
匹配0个字符,这会阻止我们在回溯时进行大量的工作

^[a-zA-Z]+(\\s+\\w*)*\(\\w+\\)
或^[a-zA-Z]+\\w*(\\s+\\w*)*\\(\\w+\\)
我还建议在这两种情况下都从
[a-zA-Z]
中删除
+
,因为
\\w*
已经涵盖了这一点(因此不会更改正则表达式匹配的内容),并且(在我看来)在查看它时会使正则表达式的所需行为更加清晰


PS:
[\\s]*
相当于在转义的开头圆括号之前添加
\\s*
。此外,删除字符类速记周围所有无用的方括号,并将
([\\s]*[\\w]*)*
更改为
(?>\\s+\\w+)*
或更好的
(?:\\s+\\w+)*++
(这个想法是为了避免灾难性的回溯)对于
asdasdassd sadsdasd(s)sdsd
?没有匹配或
asdasdasdasd sadsdasd(s)
?如果没有匹配,你需要使用
匹配([a-zA-Z]+(?:\\s+\\w+)*+\(\\w+\\)
。这是什么意思?:但是,它不起作用。当我输入^[a-zA-Z]+(\s*\w*)*(\w+)时,我可以匹配不以右括号结尾的文本。我尝试在正则表达式的末尾输入char$,它现在可以工作了。但是非常奇怪……我需要文本的格式是“任何以括号内文本结尾的字符串。没有其他内容。”