该代码实际上在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(.+?)

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

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


if ( (regex findFirstIn log).nonEmpty ) { do something }
val allGroups = regex.findAllIn(log).matchData.toListif (allGroups.nonEmpty) { do something }
if (regex.pattern.matcher(log).matches()){do something}


当上面提到的正则表达式添加到正则表达式列表中时,这三者均表现不佳。有什么建议可以改善正则表达式的性能或更改正则表达式本身吗?

标记为duplicate的Q / A有一个链接,我觉得很难理解。如果所引用的软件regexbuddy是免费的,或者至少在Mac上可以运行,则遵循本文可能会更容易。

我尝试使用负数前瞻,但无法弄清楚如何对字符串取反。代替/(.+?)#011/的是类似/([^#011]+)/的东西,只是说“#”或“ 0”或“ 1”取反。如何取消“#011”?即使在那之后,我不确定否定是否可以解决我的性能问题。

最佳答案

最简单的方法是在#011上分割。如果要使用正则表达式,则确实可以取反字符串,但这很复杂。我会去一个原子团

(?>(.+?)#011)


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

否定字符串

#011的补码是不是以#开头,不是以#开头且不以0开头,还是以两者开头且不跟随的任何东西...您知道。我添加了一些空白以提高可读性:

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


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

一种替代方法是使用负前瞻:如果以下字符不是(?!#011),则#011匹配,但不吃任何东西,因此我们使用.来吃一个字符:

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


与仅使用原子组相比,这一切都非常复杂,并且性能可能较差。

最佳化

在我上面的正则表达式中,第一个是最好的。但是,正如Casimir et Hippolyte所写,还有改进的余地(系数1.8)

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


它并不像看起来那么复杂。首先以原子方式匹配任何数量(包括零)的非#(后跟+)。然后匹配一个#后面不跟011,再匹配任意数量的non- #。重复最后一句话任意次数。

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

09-05 03:51