代码实际上在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秒。如果我添加上面的表达式,批量需要一个小时!
在我的代码中,我尝试了几种匹配正则表达式的方法:
if ( (regex findFirstIn log).nonEmpty ) { do something }
val allGroups = regex.findAllIn(log).matchData.toList
if (allGroups.nonEmpty) { do something }
if (regex.pattern.matcher(log).matches()){do something}
当上面提到的正则表达式添加到正则表达式列表中时,所有三个都表现不佳。有任何提高正则表达式性能或改变正则表达式本身的建议吗?
标记为duplicate的Q / A有一个我很难理解的链接。如果引用的软件regexbuddy是免费的或至少在Mac上工作,则可能更容易理解文本。
我尝试了否定的预测,但我无法弄清楚如何否定一个字符串。不是/(.+?)#011/
,而是/([^#011]+)/
,而只是说“#”或“0”或“1”。我怎么否定“#011”?即使在那之后,我也不确定否定是否能解决我的性能问题。
答案 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和任意数量的非#
。多次重复最后一句。
它的一个小问题是它也匹配一个空序列,我看不到一个简单的方法来修复它。