如何使用正则表达式找到具有可变长度前缀的重叠匹配?

2024-04-27 20:33:34 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试使用正则表达式匹配一些代码文件中注释内的TODO等标记。例如,考虑以下文件:

foo bar # TODO
bar foo quux   # TODO bar # TODO foo quux
quux # foo ## TODO foo # bar #  TODO quux
'# TODO\'' # TODO

注意,一行中可能有多个标记,只要每个标记前面有#,那么第二行和第三行应该匹配两次。此外,第一#(实际代码)之前的前缀可以具有任意长度;这同样适用于每个TODO后面的内容。除此之外,可能还有像# TODO这样没有注释的子字符串(参见第四行;它应该匹配一次,最后的# TODO

我一直在Stackoverflow和其他网站上搜索,但似乎没有任何东西能够回答这样一个问题:在这些匹配之前有多个重叠的匹配项和一个可变长度的前缀。我认为问题主要在于试图结合上下文使用积极的lookaheads/lookbehinds:

  • (?=#\s*TODO[^#]*)不起作用,因为它两次匹配第四行。这就是为什么我说重叠:似乎在匹配时必须考虑前缀的结构
  • 我可以将前缀(实际代码和没有标记的注释)部分与^[^#']*('[^'\\]*(\\.[^'\\]*)*'[^#']*)*(#\s*(?!TODO)[^#]*)*匹配,这样我就可以正确地得到第四行,但这是一个可变长度的匹配,因此,就我所知,使用像(?<=^[^#']*('[^'\\]*(\\.[^'\\]*)*'[^#']*)*(#\s*(?!TODO)[^#]*)*)(#\s*TODO[^#]*)这样的正向查找将导致每个正则表达式引擎上出现错误(如果工作,无论如何只会匹配第一个# TODO
  • 匹配前缀然后使用像^[^#']*('[^'\\]*(\\.[^'\\]*)*'[^#']*)*(#\s*(?!TODO)[^#]*)*(?=(#\s*TODO[^#]*)(#\s*(?!TODO)[^#]*)*)这样的正向前瞻也不起作用,因为它只匹配# TODO的一个匹配项

解释:\\.匹配转义字符,[^'\\]*任何不是转义字符和字符串分隔符的内容,因此'[^'\\]*(\\.[^'\\]*)*'匹配任何字符串文字。在字符串文字部分之外使用[^#']*意味着:匹配任何不以字符串或注释开头的内容,因此行的代码部分是^[^#']*('[^'\\]*(\\.[^'\\]*)*'[^#']*)*。不包含标记的注释段可以通过#\s*(?!TODO)[^#]*找到,因此整个前缀可以与^[^#']*('[^'\\]*(\\.[^'\\]*)*'[^#']*)*(#\s*(?!TODO)[^#]*)*匹配

我使用ripgrep,因此这适用于PCRE/PCRE2正则表达式。 然而,我对任何regex方言是否有解决方案感兴趣

我知道我可以匹配至少有一个正确匹配的行,并用一些脚本语言对结果进行后期处理,以从行中提取每个TODO,但我想知道是否可以只执行此正则表达式


Tags: 文件字符串代码标记内容foo网站bar