Vim正则表达式中的'\ zs'和'\ @ <='原子有什么区别?


11

这是我从文档中得到的结果:\zs匹配前面的正则表达式后\@<=“启动突出显示的部分”,而匹配前面的原子后“启动突出显示的部分” 。但是我不完全理解这一点的微妙之处,所以谁能解释他们在深度上有何不同?

这就是让我感到好奇的原因:如果我跑步

/\_s\zsnnoremap

即选择nnoremap由空格或一个开始的行之前(即,从前述线路换行,因此\_之前的s),然后运行gn到进入的视觉模式和直观地选择下一个匹配,由于某种原因,在第一列(即第一nnnoremap)选择-尽管事实上,整个nnoremap凸显:hlsearch开启。

但是,如果我改为运行搜索

/\_s\@<=nnoremap

然后尝试gnnnoremap正确选择了整个。这可能是怎么回事?我(敢说)发现了一些晦涩的错误吗?


我认为它是存在的,:h patterns但我的记忆表明,正则表达式由原子组成,如果这有助于解释差异。
D. Ben Knoble

Answers:


15

看起来您确实发现了一个晦涩的错误。我gn早在2012年就为Vim 7.3 实现了textobject。它基本上以以下方式工作:

1)向后搜索当前正则表达式的最后一个匹配项。

2)向前搜索当前正则表达式的下一个匹配项。

这应该明确,光标将位于下一个匹配项的开始处,即使它已位于1)的开始处。 最后

3)搜索当前正则表达式的结尾。并将光标放在此处。

现在,这里发生的是,对当前比赛结束的搜索将回绕并移回到上一场比赛的结束(因为wrapscan在设置为1后已被设置)。然后,将可视标记设置为从起点(点2的终点)到下一个搜索项3所移动到的区域。

我将仔细研究该问题,稍后可能会为Vim提交补丁。

[更新22.05.2018]我已经编写并提交了补丁来解决此问题。

[Update2 22.05.2018] 补丁已合并为补丁级别8.1.0018

[更新22.10.2019] 从Vim补丁8.1.629开始,不再执行第三步。现在,Vim现在可以在找到比赛开始时确定比赛结束(第2步)


8

克里斯蒂安完全解决了越野车的问题gn,但是\zs和之间仍然存在基本差异\@<=。最大的是\@<=修饰前一个原子,而\zsiself中的原子是。

考虑:

Xnnoremap

\%1cX\zsnnoremap     (regex 1)
\%1cX\@<=nnoremap    (regex 2)
\%2cX\@<=nnoremap    (regex 3)

正则表达式1匹配,因为\%1c匹配列1并且那里有一个X。 \zs只是导致比赛在X之后的位置重新开始。

正则表达式2不匹配,因为尽管\%1c匹配第一列,但X\@<=宽度为零(如文档中所述),并且nnoremap从第2列开始。没有什么可以弥补第1列和第2列之间的位置差。

正则表达式3匹配,因为它nnoremap从第2列开始。


1
我不认为正则表达式2会失败,因为没有什么可弥补第1列和第2列之间的位置差异nnoremap。但是即使没有,正则表达式仍然会失败。我认为它失败是因为\%1cX\@<=表达了一个不存在的立场。\%1c匹配第1列的位置,并X\@<=要求X在此之前匹配一个字符。但是第一列之前不能有任何字符。这就是为什么即使您X用点(任何字符)替换,正则表达式\%1c.\@<=仍然会失败。
user938271

4

\zs适用于整个正则表达式,并将下一个字符设置为整个匹配的第一个字符。之前的任何内容\zs都不会包含在匹配文本中。

\@<=另一方面,仅影响直接在其周围的原子,从而允许您指定下一个原子仅在前一个原子之后才匹配。因此,例如,正则表达式:

\vbar.*(foo)@<=bar

将匹配的两个实例之间的所有文本bar(包括实例本身),但前提是第二个实例以开头foo。即,它将匹配:

barbazfoobar

但不是:

barbazbazbar

因为\@<=是以这种方式本地化的,所以您甚至可以\@<=在一个表达式中多次使用:

\vbar.*(foo)@<=bar.*(foo)@<=bar

以下将匹配的三个实例bar,但前提是后两个实例均以开头foo

即给出文本:

barfoobarbazfoobar
barfoobarbazbazbar
barbazbarbazfoobar

它只会匹配第一行。


但是你可以交换第一回顾后\zs,即,这也应该工作:\vfoo\zsbar.*(foo)@<=bar
卡尔·英格夫·勒沃格

@KarlYngveLervåg好点。我进行了编辑,以使区别更加清晰,并使用\zs根本无法替代的示例。
丰富

因此,据我所知,\zs并且\ze可以被环视正则表达式模式所代替,它们更强大,对吗?更强大的功能是,它们可以多次使用,并且可以与分组\(\)。也因为它们像Perl的正则表达式一样工作。有什么事吗
克劳斯,

1
@klaus这听起来对我来说是正确的(尽管我不是专家)。请注意,您应该在可能的情况下使用\zs/ \ze,因为它们比环顾四周的速度更快。
Rich

明白了 并且\zs\ze显然更直观。感谢您的解释。
克劳斯,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.