如何在没有过多回溯的情况下匹配像 /\s*a\s*b/ 这样的正则表达式?

How can I match regexps like /\s*a\s*b/ without excessive backtracking?

我正在使用 Perl,它使用回溯正则表达式引擎。

我需要匹配以空格分隔的标记字符串(我正在解析汇编程序以防有人疑惑)。我目前正在使用像

这样的正则表达式
s/(\.text\n\s*\.align .(?:,0x90)?\n)\.globl\s+.*_?__stg_split_marker.*\n//m

想这样做,但担心回溯过多。

我怎样才能避免这种情况?

一般方法是,对于任何可能执行您想禁止的回溯的子表达式,用 (?>...) 包围子表达式。 例如,\s+ 将是 (?>\s+)

根据我的经验,很多人都尝试在 not required, and many people try to avoid them where they're the best solution 的地方使用正则表达式。所以我必须总是先问 - 你想做什么?

在我看来,您正试图拆分一些代码。也许将其反转并始终将其拆开然后将其组合为制作过程的一部分会更容易吗?对于这类事情,我经常会使用模板让我以完全正确的方式构建代码,并为这个构建单元插入特定的代码。然后我完全避免了正则表达式问题,这意味着我也可以避免开发人员在 6 个月后做一些我没想到的事情。

老实说,该代码的回溯应该很少。整个事情由.text锚定,其他可能发生回溯的地方会很快中止。不过,您可以尝试一些优化。

  • 通过使用 \K 消除了捕获(这会减慢速度)的需要。鉴于我上面所说的,这可能是提供最大收益的优化。
  • \s* 替换为 \s*+ 又名 (?>\s*)(防止回溯,这是安全的,因为下一个字符不能是 space)。
  • 将最后的 .* 替换为 .*+ 又名 (?>.*)(防止回溯,这是安全的,因为下一个字符不能是非换行符)。
  • (?:,0x90)?替换为(?:,0x90)?+(防止回溯,这是安全的,因为下一个字符不能是逗号)。
  • \s+.*_? 替换为更简单但等效的 \s.*

s/
    \.text \n
    \s*+
    \.align [ ] .(?:,0x90)?+ \n
    \K
    \.globl \s .* __stg_split_marker .*+ \n
//xm