Java花了太长时间来确定字符串是否与String.matches匹配

时间:2018-01-12 09:47:43

标签: 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(text.matches(pattern)) - it works
System.out.println(text2.matches(pattern)) - never ending story

有什么问题?

2 个答案:

答案 0 :(得分:0)

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)

答案 1 :(得分:0)

由于你的正则表达式中的*,第二个需要很长时间(或者至少需要很长时间,具体取决于实现)。

你的正则表达式开始试图像这样匹配:

[a-zA-Z]+   \s* \w*      \s* \w*   \s* \w* \( \w+ \) [unmatched]
Asdadasdasd     sadsdsad     sdasd     X   (  s   )  sdsd

此时你可能会说它"好吧,不匹配,我们已经完成了#34;。

但这不是它的作用。

相反,它会backtrack试图找到一个可行的匹配(因为在这种情况下计算机找出回溯将是浪费时间并不是那么容易)。

以前匹配第二个\w*sdasd的位置,它现在会尝试少一个字符,即sdas,然后它会添加另一个匹配的\s*\w* \s*为{0}个字符,d\w*

[a-zA-Z]+   \s* \w*      \s* \w*  \s* \w* \s* \w* \( \w+ \) [unmatched]
Asdadasdasd     sadsdsad     sdas X   d       X   (  s   )  sdsd

这也不会起作用,所以它会尝试sda然后再sd,它不会工作并导致它将其进一步分解为{{1} },sdas

d

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

PS:以上并不一定完全它的作用,它更倾向于对发生的事情有一个基本的了解。

PPS:为简洁起见,使用\w代替\

你如何解决它?

有几种方法可以解决它。

要求变化最小的那个可能是使用\\代替(\\s*\\w*)*+制作*+ possessive,这会阻止它完全回溯(这是排队的)我们想要的东西)。

*

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

这可以修复它,因为我们无法再为\\w*匹配0个字符,这会阻止我们在回溯时会做很多工作。

\\s

我也建议在任何一种情况下都从 ^[a-zA-Z]+(\\s+\\w*)*\\(\\w+\\) OR ^[a-zA-Z]+\\w*(\\s+\\w*)*\\(\\w+\\) 删除+,因为[a-zA-Z]已经涵盖了这一点(因此不会改变正则表达式匹配)和(在我看来)使正则表达式的期望行为在看时更清晰。

PS: \\w*相当于[\\s]*