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

标签 java regex performance non-greedy

代码实际上是在 Scala (Spark/Scala) 中,但根据文档,库 scala.util.matching.Regex 委托(delegate)给 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 ) { 做某事 }

  2. val allGroups = regex.findAllIn(log).matchData.toList if (allGroups.nonEmpty) { 做某事 }

  3. if (regex.pattern.matcher(log).matches()){做某事}

当上面提到的正则表达式添加到正则表达式列表中时,所有三个都表现不佳。对于提高正则表达式性能或更改正则表达式本身有什么建议吗?

标记为 duplicate 的问答有一个我发现很难理解的链接。如果引用的软件 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 的 #,并再次匹配任意数量的非 #。重复最后一句话任意多次。

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

关于Java,惰性表达式的正则表达式性能较差,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30266109/

相关文章:

c# - 高水平的 .NET 争用率和 CLR 安全 RT 检查是否是由过多的 SQL 调用引起的

java - 生成一个 Java 可用的正则表达式,在精确位置否定单词

regex - Cygwin Sed - 一个在开头匹配字符串但在结尾不匹配的表达式

java - 如何通过 RequestFactory 高效使用 RestTemplate?

java - ADAL -如何以编程方式检索 oAuth 访问 token

python - 匹配并索引所有子字符串,包括重叠的子字符串

python - 当语料库有 100 亿个独特的 DNA 序列时,如何使用 BK 树实现快速模糊搜索引擎?

java - Android,从azure表中检索经过身份验证的用户信息

java - 如何构建全局序列比对的评分矩阵?

javascript - 如何在java中将NCPDP标准格式消息转换为xml格式?