Java,使用惰性表达式的正则表达式性能差

时间:2015-05-15 18:09:59

标签: java regex performance non-greedy

代码实际上在Scala(Spark / Scala)中,但根据文档,scala.util.matching.Regex库委托给java.util.regex。

代码本质上从配置文件中读取一堆正则表达式,然后将它们与提供给Spark / Scala应用程序的日志进行匹配。一切正常,直到我添加一个正则表达式来提取由制表符分隔的字符串,其中制表符已被展平为“#011”(由rsyslog提供)。由于字符串可以有空格,我的正则表达式如下所示:         (.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)#011(.+?)

当我将此正则表达式添加到列表中时,该应用程序将永远完成处理日志。为了让您了解延迟的大小,一百万行的典型批次在Spark群集上匹配/提取的时间不到5秒。如果我添加上面的表达式,批量需要一个小时!

在我的代码中,我尝试了几种匹配正则表达式的方法:

  1. if ( (regex findFirstIn log).nonEmpty ) { do something }

  2. val allGroups = regex.findAllIn(log).matchData.toList if (allGroups.nonEmpty) { do something }

  3. if (regex.pattern.matcher(log).matches()){do something}

  4. 当上面提到的正则表达式添加到正则表达式列表中时,所有三个都表现不佳。有任何提高正则表达式性能或改变正则表达式本身的建议吗?

    标记为duplicate的Q / A有一个我很难理解的链接。如果引用的软件regexbuddy是免费的或至少在Mac上工作,则可能更容易理解文本。

    我尝试了否定的预测,但我无法弄清楚如何否定一个字符串。不是/(.+?)#011/,而是/([^#011]+)/,而只是说“#”或“0”或“1”。我怎么否定“#011”?即使在那之后,我也不确定否定是否能解决我的性能问题。

1 个答案:

答案 0 :(得分:2)

最简单的方法是拆分#011。如果你想要一个正则表达式,你确实可以否定字符串,但这很复杂。我会去一个原子团

(?>(.+?)#011)

一旦匹配,就不再有回溯了。做完并期待下一组。

否定字符串

#011的补充内容不是以#开头,或者以#开头,而不是0,或者以{2}开头而不是跟着......你知道。为了便于阅读,我添加了一些空白:

 ((?: [^#] | #[^0] | #0[^1] | #01[^1] )+) #011

太可怕了,不是吗?与原始表达式不同,它与换行符匹配(您没有具体说明它们)。

另一种方法是使用否定前瞻:(?!#011)匹配iff以下字符不是#011,但不吃任何东西,所以我们使用.来吃一个字符:

 ((?: (?!#011). )+)#011

这一切都非常复杂,而且很可能不如简单地使用原子组。

优化

出于我的上述正则表达式,第一个是最好的。然而,正如Casimir et Hippolyte写的那样,还有改进的余地(因子1.8)

( [^#]*+ (?: #(?!011) [^#]* )*+ ) #011

它并不像看起来那么复杂。首先匹配任何数字(包括零)的非#原子(尾随+)。然后匹配#后跟不是011和任意数量的非#。多次重复最后一句。

它的一个小问题是它也匹配一个空序列,我看不到一个简单的方法来修复它。