通过利用:substitute
命令中的表达式替换功能(参见:help sub-replace-\=
),有一种在文本中收集模式匹配的通用方法。关键思想是使用一个替换枚举所有的模式匹配来计算一个表达式,将它们存储而不进行替换。
首先,让我们考虑保存匹配项。为了保留一系列匹配的文本片段,使用列表(参见:help List
)很方便。然而,不能直接使用:let
命令修改列表,因为没有办法在表达式中运行Ex命令(包括\=
替换表达式)。但是,我们可以调用其中一个能够就地修改列表的函数,例如,将给定项附加到列表的add()
函数(参见:help add()
)。
另一个问题是如何在运行替换时避免文本修改。一种方法是始终使模式具有零宽度匹配,方法是在其前面加上\ze
或在其后面添加\zs
原子(参见:help /\zs
,:help /\ze
)。以这种方式修改的模式捕获了原始模式在文本中前面或后面的空字符串位置(这些匹配在Vim中称为零宽度匹配,参见:help /zero-width
)。然后,如果替换文本也为空,则替换实际上不会改变任何内容:它只是用一个空字符串替换了零宽度匹配。
由于add()
函数和大多数修改列表的函数一样,返回更改后列表的引用,因此为了使我们的技术起作用,我们需要以某种方式从中获取一个空字符串。最简单的方法是通过指定索引的范围来从中提取长度为零的子列表,其中起始索引大于结束索引。
结合上述想法,我们得到以下Ex命令:
:let m=[] | %s/\<case\s\+\(\w\+\):\zs/\=add(m,submatch(1))[1:0]/g
执行后,第一子组的所有匹配都将累积在变量m
引用的列表中,并且可以直接使用或以某种方式进行处理。例如,在插入模式下逐行粘贴列表内容的方法如下:
Ctrl+R=m
Enter
要在普通模式下执行相同操作,只需使用:put
命令即可:
:put=m
从7.4版本开始(参见:helpg Patch 7.3.627
),Vim会在替换命令的替换字符串中的每个匹配项上评估一个\=
表达式,即使有n
标志(该标志指示Vim仅计算匹配项的数量而不进行替换-请参见:help :s_n
)。在这种情况下,表达式的求值结果并不重要,因为由于在计数过程中没有进行替换,所以最终结果会被丢弃。
这使我们能够利用表达式的副作用,而无需担心在该过程中保留缓冲区的内容,因此可以省略所有零宽度匹配和空子列表索引的技巧:
:let m=[] | %s/\<case\s\+\(\w\+\):/\=add(m,submatch(1))/gn
方便的是,运行此命令后,该缓冲区甚至不会被标记为已修改。
:%s/^\vcase ([^:]+):/\1/
使用\1
来获取第一个捕获组。 - mathematical.coffeesed -n '/^\s*case\s\+/{s/\s*case\s\+\([^:]\+\):/\1/;p}' file
。 - beerbajay