如何在vim可视模式下搜索/替换第n个出现的位置?

时间:2020-04-27 14:45:03

标签: regex vim

这有效:

'<,'>s/\v\/\zs(\/)// 
'<,'>s/\v(\/)@<=\//BAR/

我只是想知道是否有一种更简单的方法用{}或vim中的某些内容替换第n个出现的东西。

替换第三个正斜杠“ /”。

/dir1//fas//fooBar/¬
/dir2//\.foobar//fas/¬
/dir//.foo//fas/¬

我将如何替换第四个“ foo”?

foo foo foo foo foo foo foo
foo foo foo foo foo foo foo

1 个答案:

答案 0 :(得分:1)

为简单起见,我将讨论如何匹配这些模式,在:s命令上使用相同的模式,在替代命令中替换它们应以相同的方式工作。

替换第三个正斜杠“ /”。

对于一个字符的匹配,这更容易,因为您可以使用[^/]查找不属于该匹配的字符。

如果要计算匹配数,则需要从该行的开头开始,因此以^定位。

到那时,您可以匹配两个“非斜杠”实例,然后匹配一个“斜杠”,然后在第三个实例上,您可以使用\zs将其标记为实际匹配的开始。

不幸的是,如果我们在比赛中使用/,则\/本身需要用/\v^%([^\/]*\/){2}[^\/]*\zs\/ 进行转义,但是结果是:

/

包含?的模式的一个常见技巧是改为使用?\v^%([^/]*/){2}[^/]*\zs/ 向后搜索,所以我们可以这样做以提高可读性:

(...)

我在这里使用的模式模式项可能是一些陌生的:

  • %(...):对模式进行分组,与\zs相同,但不创建捕获组。
  • {2}:与前面的模式完全匹配两次。

请记住,我们使用的是"verymagic" with \v,因此上述大多数内容都不需要反斜杠。

我们可以采取一种简洁的捷径来缩短上面的模式(当我们考虑较长单词的情况时,这将对我们有所帮助),也就是说,如果您的模式中的多个位置都有?\v^%([^/]*\zs/){3} ,那么最后一个要匹配的将是定义实际比赛开始的那个。 (请参见:help /\zs。)

所以我们可以简化为:

\zs

我们先匹配“非斜杠”,再匹配“斜杠”三遍。 [^...]仅在最后(第三个)匹配项上生效,因此您将最终匹配该行上的第三个斜杠。

现在让我们继续讨论匹配单词的更复杂的情况:

我将如何替换第四个“ foo”?

在这里,我们不能使用\v([^f]|f[^o]|fo[^o])来匹配“ not foo”。我的意思是,我们可以使用类似/\v^%(%(.%(foo)@<!)*\zsfoo){4} 的名称,但是随着您所匹配的单词的增长,它会迅速增长。还有一种更好的方法。

我们可以使用零宽度的负向后看!有关这个有趣的运算符,请参见:help /\@<!。简而言之,它采用了前面的原子(我们将在此处使用带有单词的组),并确保该项目与在该位置结束的匹配。

所以我们可以使用它:

%(foo)@<!

此处的.确保我们匹配的每个o不会是foo中的最后一个foo。这样,我们就可以准确地计算行中的第一,第二,第三和第四\zs,并确保我们不会匹配第五,第六或第七。

这又是我们的技巧,将其重复四次(以找到第四场比赛)并握住最后一支*

请注意,负向后看对固定单词效果很好,但是如果您开始使用+fofo等复数,则情况会变得更加复杂。查看对操作员的帮助以及警告可能会变慢的警告。运算符还有一个变体,它限制了它返回的字符数,当您匹配一个固定的单词时,您并不一定要使用它,但是对于更通用的匹配而言可能会有所帮助。

与此有关的一个有趣的测试用例是具有重复项的匹配项,例如fofofo,以及包含重复项的文本,例如fofofofofofofo

事实上,通过对这些代码的测试,我发现上面的模式实际上更愿意匹配fofo中的第二个匹配项,而不是第一个匹配项,如果那是该行中*的第四次匹配项。这是因为/\v^%(%(.%(foo)@<!){-}\zsfoo){4} 运算符是贪婪的。我们可以改用{-}来解决此问题,它与可能的最短序列匹配。

修复该错误,我们得到:

You will understand from the links.

这很通用,您可能可以使用任何固定的单词,甚至可以使用带有一些变体的模式(例如大小写,复数形式,替代拼写等)

相关问题