在vim正则表达式中,有没有一种方法可以进行负向前瞻?

66
在 Vim 中,是否有一种方法可以搜索包含 abc 但不在同一行中包含后续出现的 xyz 的行?那么以下几行将匹配:
The abc is the best
The first three letters are abc

以下内容将不匹配:

The abc is the best but xyz is cheaper
The first three letters are abc and the last are xyz

我了解以下这样的语法:

/abc\(xyz\)\@!

但这只会避免匹配abcxyz,而不是在它们之间有任何东西的情况,例如abc-xyz。使用

/abc.*\(xyz\)\@!

由于在行的后面有许多位置未匹配到xyz,因此这也不起作用。

(我应该指出,在命令行上我会执行类似于grep abc <infile | grep -v xyz的操作,但我想在Vim中交互式地执行上述操作。)


1
为什么要使用 <infile 而不是只用 infile - Good Pen
6个回答

69

正则表达式中的.*表示匹配任意字符,且其前后可能存在非匹配字符,后续可以通过断言进行验证。
你需要将.*放入负向预查中:

/abc\(.*xyz\)\@!

可能的原因
尝试对.*的所有可能匹配进行非匹配,并且只有在尝试了所有分支后,才声明\@!已经被满足。


4
这相当于Perl样式引擎中的abc(?!.*xyz) - nhahtdh
这个方法可行,因为前瞻会从当前位置启动另一个全面的搜索来查找模式,并使用结果来决定是否继续(取决于前瞻是正向还是负向)。 - nhahtdh
2
请参阅:help perl-patterns。对于未在pattern1之前的pattern2,即负向后查找,请搜索\(pattern1\)\@<!pattern2 - hochl
这可能已经过时了 - 但在我看来,上面的解决方案对于像 xyzabc 这样的字符串不起作用。OP 表示一行中不应该任何地方xyz。对我有效的是强制捕获整行:^(?!.*xyz).*abc(?!.*xyz)(不是 vim 格式)。请参见 RegExr - Guy
这个帮助我在 Terraform 计划中搜索更改行 /\("[^"]*"\) => \(\1\)\@! - Bruno Bronosky

25

我总觉得在vim中需要转义括号很奇怪,所以我大部分时间尝试使用“非常魔法”模式,可以通过使用\v来激活:

/\vabc(.*xyz)@!

2
太好了,知道这个对我来说非常重要,以前我曾经陷入过VIM-Regex的地狱。 - polynomial_donut

10

虽然你的问题是关于向前查看(lookahead)的,但我在搜索向后查看(lookbehind)时找到了它。因此,我发表了解决方案,以便其他人可以找到。

如果你搜索未以 pattern1 开头的 pattern2,也就是负向向后查看(negative lookbehind),请搜索:

\(pattern1\)\@<!pattern2

2
这相当于 PCRE 中的 (?!pattern1)pattern2 - Mateen Ulhaq

2

这对我有用 /abc\(.*xyz\)\@!


1

如果您不仅限于使用vim正则表达式搜索,还有另一种选择。您可以使用以下全局命令:

:g/abc/v/xyz/p

说明

  • "g/abc":扫描所有行并标记所有匹配该模式的行
  • v/xyz/:从上述结果中标记不包含“xyz”模式的所有行
  • p:打印找到的行。您可以将其替换为另一个命令

0

我认为我会将其写成/abc\(\(xyz\)\@!.\)*$

我不确定这是否比其他建议更快(我认为它可能是因为它只测试每个位置一次),但从概念上讲,它是这样的:

“abc后跟任何不是xyz的东西,多次直到行尾”

我喜欢这种方式胜过其他提到的模式中的“abc后跟任何不跟随xyz”的方式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接