regex 如何使用Vim提取文件中的所有正则表达式匹配项?

rsl1atfo  于 2023-02-17  发布在  其他
关注(0)|答案(5)|浏览(535)

请看下面的例子:

case Foo:
    ...
    break;
case Bar:
    ...
    break;
case More: case Complex:
    ...
    break:
...

比如,我们想检索正则表达式case \([^:]*\):的所有匹配项(整个匹配文本,或者更好的是,\(\)之间的部分),这应该会给我们(最好是在一个新的缓冲区中)如下所示:

Foo
Bar
More
Complex
...

另一个用例示例是提取HTML文件的某些片段,例如图像URL。
有没有一种简单的方法可以收集所有的正则表达式匹配项,并将它们放到Vim中的一个单独的缓冲区中?

myss37ts

myss37ts1#

有一种通用的方法可以收集一段文本中的模式匹配。该技术利用了:substitute命令的表达式替换功能(参见:help sub-replace-\=)。关键思想是使用枚举所有模式匹配的替换来计算一个表达式,该表达式存储了这些模式匹配而没有替换。
首先,让我们考虑保存匹配。为了保持匹配文本片段的序列,使用列表是很方便的(请参见:help List)。但是,由于无法在表达式中运行Ex命令,因此无法使用:let命令直接修改列表(包括\=替代表达式)。然而,我们可以调用其中一个函数来修改列表,例如,add()函数,它将给定的项添加到列表中(参见:help add())。
另一个问题是如何避免在执行替换时修改文本,一种方法是通过预先添加\ze或附加\zs原子来使模式始终具有零宽度匹配(参见X1 M9 N1 X,以这种方式修改的模式捕获文本中原始模式出现之前或之后的空字符串(这样的匹配在Vim中被称为 * 零宽度 * 匹配;参见:help /zero-width)。然后,如果替换文本也为空,则替换实际上不会改变任何内容:它只是用空字符串替换零宽度匹配。
由于add()函数和大多数列表修改函数一样,返回对修改后的列表的引用,因此为了使我们的技术能够工作,我们需要以某种方式从中获取一个空字符串。最简单的方法是通过指定一个索引范围,使起始索引大于结束索引,从而从中提取一个零长度的子列表。
结合上述思想,我们得到如下Ex命令:

:let m=[] | %s/\<case\s\+\(\w\+\):\zs/\=add(m,submatch(1))[1:0]/g

执行后,第一个子组的所有匹配项都累积在变量m引用的列表中,可以按原样使用或以某种方式处理。
Ctrl+R =m回车键
要在正常模式下执行相同操作,只需使用:put命令:

:put=m

从版本7.4开始(参见:helpg Patch 7.3.627),Vim为模式的每个匹配项计算替换命令的替换字符串中的\=表达式,即使给出了n标志(这指示它只计算匹配的数量而不进行替换-参见:help :s_n)。在这种情况下,表达式的计算结果并不重要,因为在计数期间没有发生替换,所以无论如何都要丢弃结果值。
这允许我们利用表达式的副作用,而不必担心在处理过程中保留缓冲区的内容,因此可以省略所有零宽度匹配和空子列表索引的诡计:

:let m=[] | %s/\<case\s\+\(\w\+\):/\=add(m,submatch(1))/gn

方便的是,在运行此命令后,缓冲区甚至不会被标记为已修改。

yc0p9oo0

yc0p9oo02#

虽然不可能编写一行程序来完成示例,但是交互式地键入:%s/case \([^:]*\):/\=.../这样的命令是很困难的。
我更喜欢按照以下步骤使用vim-grex
1.使用/检查正则表达式是否与预期行匹配。例如:/^\s*\<case\s\+\([^:]*\):.*$<Enter>
1.执行:Grey,它将删除与当前搜索模式匹配的行。
1.通过:new等打开一个新的缓冲区。
1.按p等放置被拉绳。
1.将不感兴趣的部分修剪:%s//\1/

dtcbnfnu

dtcbnfnu3#

    • 如何使用vim regex从下面的行中提取单词,假设'help'可以是任何单词,如'rust'或'perlang'。**
vim:tw=78:ts=8:ft=help:norl:
    • 解决方案:**
let foo = substitute(foo, '^\s*vim:.*:ft=\([a-z]\+\).*:\s*$', '\1', '')
echo "foo: '" . foo . "'"
    • 打印:**
foo: 'help'
    • 导师冥想:这是怎么回事?**

取变量foo中的字符串,匹配它以Assert行首,然后是任意数量的空格,文字vim和一个冒号,然后是任意数量的任意字符,冒号ft=后面是任意字母,然后是任意内容,Assert行以冒号结尾,将所有内容放入名为1的寄存器中,然后在参数2中重新获得该字符串,substitute使用该字符串替换先前的字符串。
作为一个普遍的哲学,任何正则表达式超过你的手指在屏幕上是一个史诗般的失败,所以降低屏幕分辨率,直到它适合。

d8tt03nd

d8tt03nd4#

作为ib.的公认答案的一个小补充,它工作得很好,看起来n标志足以避免不必要的替换问题。

:let t=[] | %s/\<case\s\+\(\w\+\):/\=add(t,submatch(1))/gn

在s_flag帮助中:
[n]报告匹配项的数量,但实际上不进行替换。忽略[c]标志。将'report'视为零来报告匹配项。这对于计算项数很有用。如果使用\= sub-replace-expression,则每次匹配时都会在沙箱中计算表达式。

xxhby3vn

xxhby3vn5#

:g/^case\s\L\l\+\scase.*/s/case/\r&/g
:let @a=''|g/^case\s\L\l\+:/y A

现在打开一个新的缓冲区或临时文件,然后应用:

"ap
:%s_^\vcase ([^:]+):_\1_

或者,如果您不关心当前缓冲区(当然,您可以撤消此操作)(对于复杂的示例,updated):

:g/^case\s\L\l\+\scase.*/s/case/\r&/g
:v/^case\s\L\l\+:/d
:%s_^\vcase ([^:]+):_\1_

相关问题