防止在正则表达式上回溯以查找非注释行(不以缩进的“#”开头)

Posted

技术标签:

【中文标题】防止在正则表达式上回溯以查找非注释行(不以缩进的“#”开头)【英文标题】:Prevent backtracking on regex to find non-comment lines (not starting with indented '#') 【发布时间】:2018-07-14 15:16:00 【问题描述】:

我想在缩进的代码中搜索不以井号 (#) 开头的行。

目前,我正在使用正则表达式 ^\s*([^\s#].*) 并启用了多行选项。

我的问题是,在没有注释的行上它工作得很好。

由于\s* 从注释符号到行首,正则表达式引擎在注释行上执行回溯,这有时会导致 40 或 50 个回溯步骤。

正则表达式在 python 代码上完美运行。由于引擎导致的回溯,它的效率不是很高。

知道如何避免它吗?


奖励:正则表达式引擎无法识别它在\s* 中一一搜索[^\s] 并导致这么多的回溯,这很有趣。使重新引擎工作的挑战是什么?

奖励 2:仅使用 stdlib re 模块。因为我无法添加第 3 方。 (我在技术上使用 sublime text 进行搜索,但想知道如何在 Python 中进行搜索)

【问题讨论】:

@Alex erm,它正在工作。期望的输出是更少的回溯。 有什么理由不split\n 上的字符串,然后将每一行与更简单的正则表达式进行比较? 是的,我实际上在使用 sublime text 的正则表达式查找功能。不想编辑代码。 【参考方案1】:

使用atomic feature of lookarounds 避免回溯:

^(?=(\s*))\1([^#].*)
    ^^^^^  ^

这种用法在@vks 提出的否定前瞻中得到了简化。

或使用 regex 模块时的所有格量词:

^\s*+([^#].*)

甚至原子团:

^(?>\s*)([^#].*)

Sublime Text 在 PCRE 上支持所有这三个。

对于奖励部分,不,这不好笑。如果您对它更加敏锐,您会发现它不是[^\s],它实际上等于\S,但它有点不同:[^\s#] 对于引擎来说意味着它在每一步都有两条不同的路径寻找所以它回溯到一个。

【讨论】:

说什么?这是错误还是功能?为什么重新引擎不会导致环视的回溯?这甚至有记录吗?我的意思是,根据正则表达式好友,这...有效... 这就是它的工作方式。检查Lookaround Is Atomic。 最后,关于重新识别\S 不会出现在\s* 中。即使它带有英镑符号,这个组合仍然没有机会出现在 \s 内。我不精通引擎的内部工作原理,但是是否有可能以某种方式对其进行编译,以便引擎了解这是不可能的并且不会浪费时间?听起来在编译阶段消除这些选项可能是可能的(不是说这很容易,而是可能) 有一些特定于引擎的预扫描优化,它们在进入匹配过程之前甚至在处理过程中研究模式。就像当您执行 \s*\S 引擎不会尝试回溯到 \s* 以匹配非空白字符时,引擎会以占有方式匹配 \s*。但是,当有多种方法可以接近引擎时,如果不进行回溯,引擎就不会有任何想法,因此您的情况。【参考方案2】:

你可以简单地说

^(?!\s*#).*

This 只需要 6 步,而 yours 需要 33 步。

【讨论】:

好吧,我理解@revo 的回答,关于离开后不回溯到环视。为什么这不回溯 inside 前瞻?毕竟前瞻内的回溯是可能的...... 我标记他的只是因为顺便说一句的解释。你的答案也是一个很棒的答案,如果你能解释它是如何工作的,我会非常感谢你:-) 他说的可能是this kind of backtrack。 @WiktorStribiżew @revo 我想我明白了。引擎是否仅在退出原子部分之后而不是之前评估否定(!)?那是not(match()) 而不是match(not()) @Bharel 引擎在无法满足条件时回溯......在这种情况下,我的正则表达式满足第一个条件......那么为什么引擎会回溯......它可能会在不能满足时回溯find # as then 条件不满足

以上是关于防止在正则表达式上回溯以查找非注释行(不以缩进的“#”开头)的主要内容,如果未能解决你的问题,请参考以下文章

shell脚本应用正则表达式grep,sed,awk,的应用

shell脚本应用正则表达式grep,sed,awk,的应用

Python学习笔记之函数与正则

使用正则表达式解析 C 样式注释,避免回溯

如何使用正则表达式匹配不以某些字符开头或结尾的单词?

字符串的正则表达式,不以指定的子字符串结尾[重复]