【问题标题】:Regex search with pattern containing (?:.|\s)*? takes increasingly long time正则表达式搜索模式包含 (?:.|\s)*?需要越来越长的时间
【发布时间】:2015-07-08 13:12:59
【问题描述】:

我的正则表达式匹配的时间越来越长(第 5 次大约需要 30 秒),但需要应用大约 500 轮匹配。 我怀疑灾难性的回溯。 请帮忙!我该如何优化这个正则表达式:

String regex = "<tr bgcolor=\"ffffff\">\\s*?<td width=\"20%\"><b>((?:.|\\s)+?): *?</b></td>\\s*?<td width=\"80%\">((?:.|\\s)*?)(?=(?:</td>\\s*?</tr>\\s*?<tr bgcolor=\"ffffff\">)|(?:</td>\\s*?</tr>\\s*?</table>\\s*?<b>Tags</b>))";

编辑:因为不清楚(我的错):我试图通过提取两个搜索组并随后添加格式来获取一个 html 格式的文档并重新格式化。

【问题讨论】:

  • 通常的答案是:不要在 HTML 或 XML 上使用正则表达式......
  • @Bruce 你会如何解决这种问题?
  • 你有(?:.|\\s)+?,这是非常低效的。将其更改为.+? 并使用Pattern.DOTALL(或模式开头的(?s))要好得多(或者如果您不想使用dotall 标志,只需使用[\\s\\S]+?)。但是最好使用 jsoup 或其他基于 HTML 解析器的解决方案。
  • @stribizhev 谢谢你,我会检查它是否足够快,或者我是否会改变我的解决方案。实际上有70K行要匹配..

标签: java regex


【解决方案1】:

替换(?:.|\\s)+? 效率非常低,因为它涉及太多回溯。

基本上,这种模式的所有变体都非常低效:(?:.|\s)*?(?:.|\n)*?(?:.|\r\n)*? 以及贪婪的对应物((?:.|\s)*(?:.|\n)*(?:.|\r\n)*)。 (.|\s)*? 可能是最糟糕的。

为什么?

.\s 这两个选项可以匹配相同位置的相同文本,两者都至少匹配常规空格。请参阅 this demo 完成 3555 步和 .*? demo(使用 s 修饰符)完成 1335 步。

Java 中的(?:.|\n)*? / (?:.|\n)* 之类的模式通常会导致Stack Overflow issue,这里的主要问题与使用交替(已经单独导致回溯)逐个字符匹配,然后是组用未知长度的量词修改。虽然一些正则表达式引擎可以应对这种情况并且不会抛出错误,但这种类型的模式仍然会导致速度变慢并且不建议使用(仅在 ElasticSearch Lucene 正则表达式引擎中,(.|\n) 是匹配任何字符的唯一方法)。

解决方案

如果您想使用正则表达式匹配任何字符,包括空格,请使用

[\\s\\S]*?

或者使用(?s)(或Pattern.DOTALLMatcher选项)启用单行模式,然后使用.(例如(?s)start(.*?)end)。

注意:要操作 HTML,请使用专用解析器,例如 jsoup。这是一个讨论 Java HTML parsers 的 SO 帖子。

【讨论】:

  • 在 Java 中,总是使用内联标志来使点匹配任何字符。它的开销比字符类解决方案少
  • @DeVeXD3VeX:实际上,在所有情况下都是正确的:使用内置结构/类型/类可以获得最佳性能。所以,(?s). 匹配任何包含换行符的字符是更可取的。
  • @stribizhev 再次感谢!你真的加快了我的正则表达式,从每场比赛的 5 分钟成倍增长到大约 5 秒
猜你喜欢
  • 1970-01-01
  • 2019-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-04
  • 1970-01-01
  • 2021-12-20
  • 1970-01-01
相关资源
最近更新 更多