【问题标题】:How to remove the slowness of this regex?如何消除这个正则表达式的缓慢?
【发布时间】:2019-03-21 21:38:04
【问题描述】:

我有以下正则表达式:

(\d+\s+[-]\s+.*?(?=\s+-)|\d+\s+[-].*)

正则表达式将使用此文本

"Option 01 - Random phrase - Top Menu",
"Option 02 - Another Random Phrase - Su Menu",
"Option 03 - More 01 Phrase - Menu",
"Option 04 - More Phrase -",
"Option 05 - Simple Phrase"

保持这样的状态

01 - Random phrase ",
02 - Another Random Phrase ",
03 - More 01 Phrase ",
04 - More Phrase ",
05 - Simple Phrase ",

这个Regex的作用是获取以破折号开头的数字,走在最后一个破折号之前。例如:

  • dfhdjfhdjf01 - 文本文本 - dkfdçsjf

当最终没有踪迹时,基本上会发生这种情况:

  • dfhdjfhdjf01 - 文本文本 dkfdçsjf

但是,在 regex101.com 上调试此正则表达式会指责您有 63 到 122 个步骤。也就是说,这个正则表达式很慢。

在批评这个问题之前,我已经阅读了正则表达式的所有文档,我想让你知道我指的是特定的术语..一个需要解决的问题。毕竟,网站不就是为了这个吗?

告诉我,我将如何解决这个正则表达式的缓慢问题?

【问题讨论】:

  • 你想让正则表达式做什么?
  • 我对这个问题的主要批评是,在寻求简化模式的方法之前,您没有具体说明您要做什么或输入的范围是多少。
  • 为什么需要简化正则表达式?为什么122步太多了?这真的会给您带来问题吗?
  • 除非您解释要匹配的规则,否则我们无法知道它如何“更轻”。没有上下文,唯一的简化可以是删除严格冗余的模式,比如用 - 替换 [-]。除此之外的任何事情都可能会根据假设删除功能。
  • 如果您的正则表达式困扰您,请给 Expresso 一个机会:ultrapico.com/expresso.htm 这是一个免费的桌面工具,可以解释一个人的结构并协助设计和验证解决方案。我的最爱。

标签: regex


【解决方案1】:

您不必太担心您在 regex101.com 上看到的步骤,因为 C# regex 库非常可靠。如果您在 regex101 上用很长的字符串测试像 (?s)a.*?b 这样的简单正则表达式,它会报告灾难性的回溯,而它在 C# 代码中可以正常工作。

有一种方法可以改进您的模式,因为它有一些冗余:请参阅重复的 \d+\s+[-] 模式。

你只需要

\d+\s+-.*?(?=\s+-|$)

regex101RegexStorm 上查看正则表达式演示。

如果.*?(?=\s+-) 应该只在- 后面有空格的情况下匹配,请使用

\d+\s+-(?:\s.*?(?=\s+-)|.+)

查看另一个demo 1(步骤更少:))/demo 2

如果你想进一步优化它,你可能想研究导致

unroll-the-loop principle
\d+\s+-(?:\s+\S*(?:\s(?!\s*-)\S*)*|.+)

this regex demo(最少步数)。

这里,\S*(?:\s(?!\s*-)\S*)* 相当于(几乎).*?(?=\s+-|$),但效率更高,因为直到空格的块在“批次”中匹配,只有在遇到空格时才会检查连字符.

详情

  • \d+ - 1 位以上
  • \s+ - 1+ 个空格
  • - - 一个连字符
  • .*?(?=\s+-|$) - 任何 0+ 个字符,尽可能少,直到第一次出现 1+ 个空格和 - 或直到字符串的末尾。
  • (?:\s.*?(?=\s+-)|.+) - 非捕获组:
    • \s.*?(?=\s+-) - 空格,0+ 字符尽可能少,最多 1+ 空格和 -
    • | - 或
    • .+ - 字符串的其余部分。
  • \S*(?:\s(?!\s*-)\S*)*
    • \S* - 0+ 个非空白字符
    • (?:\s(?!\s*-)\S*)* - 0 次或多次重复
      • \s - 一个空格
      • (?!\s*-) - 后面没有 0+ 个空格和 -
      • \S* - 0+ 个非空白字符

【讨论】:

  • 很好的插图,恭喜。
【解决方案2】:

您也可以尝试\d+\s+-[^-]* 来获得您想要的。这是迄今为止最少的步骤。或者您可以添加\d+\s+-[^-]*(?=\s),以防您需要在- 之前剪切它。 demo

【讨论】:

  • 嗯,这真的取决于这个正则表达式应该做什么工作 - 即它应该捕获哪些子字符串。如果只需要某种匹配,我建议.*,它只需要 30 个步骤。但那将是无稽之谈。
【解决方案3】:

正如其他人在 cmets 中指出的那样,尚不清楚您的正则表达式应该做什么,因为您似乎不想从潜在匹配中捕获任何内容。但无论如何,我推荐以下正则表达式,它将选项字符串解析为其基本组件:

^[^\d]*\d+\s+-\s+.*?(?:\s+-\s+.*?)?$

从此开始,您可以在要捕获的部分周围添加括号。例如:

^[^\d]*(\d+)\s+-\s+(.*?)(?:\s+-\s+(.*?))?$

这将捕获选项编号和短划线之间的文本。对于选项 04 和 05,第三次捕获将为空。

编辑:既然问题的作者已经澄清了应该捕获哪些子字符串,我想这个简单明了的正则表达式是合适的:

\d+[^-]*-[^-"]*

它捕获选项编号,搜索第一个破折号,然后捕获下一个破折号或引号之前的所有内容:

<01 - Random phrase >
<02 - Another Random Phrase >
<03 - More 01 Phrase >
<04 - More Phrase >
<05 - Simple Phrase>

请注意,这里只是添加了尖括号以显示尾随空格。这是你想要的吗?

【讨论】:

  • 我的目标是缩短步骤
  • @sYsTeM 是的,所以只需在 regex101.com/r/vzz8Dw/1 上尝试一下 - 我的正则表达式需要 365 步,而你的需要 463 步。
  • @sYsTeM 查看我的编辑。现在已根据您最近的规范进行捕获,并且您对 regex101.com/r/vzz8Dw/1 的测试计为 103 步。
猜你喜欢
  • 2016-07-14
  • 2012-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-21
  • 2023-03-17
  • 1970-01-01
相关资源
最近更新 更多