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$,它现在可以工作了。但是非常奇怪……我需要文本的格式是“任何以括号内文本结尾的字符串。没有其他内容。”