Java-带大字符串的多行正则表达式性能不佳

时间:2018-07-27 13:55:44

标签: java regex performance

我正在尝试使用多行正则表达式来匹配给定源字符串中的所有通配符。这些字符串可以超过70,000行,并且每个项目都由新行分隔。

对于我当前的正则表达式,我似乎正经历着巨大的处理时间,我只能认为这是因为它可能构造不良且效率低下。如果我在手机上执行代码,它似乎将永远运行。

我当前的正则表达式:

(?im)(?=^(?:\*|.+\*$))^(?:\*[.-]?)?(?:(?!-)[a-z0-9-]+(?:(?<!-)\.)?)+(?:[a-z0-9]+)(?:[.-]?\*)?$

有效的通配符示例:

*test.com
*.test.com
*test
test.*
test*
*test*

我用以下命令编译模式:

private static final String WILDCARD_PATTERN = "(?im)(?=^(?:\\*|.+\\*$))^(?:\\*[.-]?)?(?:(?!-)[a-z0-9-]+(?:(?<!-)\\.)?)+(?:[a-z0-9]+)(?:[.-]?\\*)?$";
private static final Pattern wildcard_r = Pattern.compile(WILDCARD_PATTERN);

我寻找匹配项:

// Wildcards
while (wildcardPatternMatch.find()) {
    String wildcard = wildcardPatternMatch.group();
    myProperty.add(new property(wildcard, providerId));
    System.out.println(wildcard);
}

我是否可以进行一些更改来改善/优化正则表达式,还是需要经过几次运行.replaceAll才能消除所有混乱,然后再进行正则表达式匹配?

2 个答案:

答案 0 :(得分:1)

我建议您看看https://en.wikipedia.org/wiki/ReDoS#Evil_regexes

您的正则表达式包含重复的模式:

(?:(?!-)[a-z0-9-]+(?:(?<!-)\.)?)+ 

作为一个简单的例子,看看这两个例子的处理时间:exact matches与拥有extra characters at the end相比,更糟糕的是,重复{{3 }}

编辑:several times

答案 1 :(得分:1)

您需要的模式是

(?im)^(?=\*|.+\*$)(?:\*[.-]?)?[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*(?:[.-]?\*)?$

请参见regex demo

要点:

  • 第一次前瞻应该在^之后。如果在此之前,则在字符串中的每个字符之前和之后进行检查。在^之后,仅在行首执行一次
  • (?:(?!-)[a-z0-9-]+(?:(?<!-)\.)?)+部分虽然很短,但实际上会降低性能,因为(?:(?<!-)\.)?是可选模式,并且整个模式都简化为(a+)+,这是导致分类失败的一种已知类型的模式回溯,在它的右边还有其他子模式。您需要打开包装,最好的“线性”方式是[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*

其余的都还可以。

详细信息

  • (?im)-不区分大小写和多行修饰符
  • ^-一行的开头
  • (?=\*|.+\*$)-字符串应以*开头或结尾
  • (?:\*[.-]?)?-匹配*和可选.或-` char
  • 的可选子字符串
  • [a-z0-9](?:[a-z0-9-]*[a-z0-9])?-字母数字字符,后跟任意0+个字母数字字符的可选序列,或/和-,后跟字母数字字符
  • (?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*-0个或多个点序列,后跟上述模式
  • (?:[.-]?\*)?-与可选.或-char and then a *`
  • 匹配的可选子字符串
  • $-行尾。