【问题标题】:How to extract all regex matches in a file using Vim?如何使用 Vim 提取文件中的所有正则表达式匹配项?
【发布时间】:2012-01-31 12:33:40
【问题描述】:

考虑以下示例:

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

假设,我们想要检索正则表达式 case \([^:]*\): 的所有正则表达式匹配(整个匹配文本,或者更好的是,\(\) 之间的部分),这应该给我们(最好在新缓冲区)类似:

Foo
Bar
More
Complex
...

另一个用例示例是从 HTML 文件中提取某些部分,例如,图像的 URL。

有没有一种简单的方法来绘制所有正则表达式匹配并将它们放入 Vim 的缓冲区中?

注意:类似于问题“How to extract text matching a regex using Vim?”。但是,与该问题中的设置不同,我也有兴趣删除不匹配的行,最好不要使用庞大或复杂的正则表达式。

【问题讨论】:

  • 你的意思是反向引用吗? :%s/^\vcase ([^:]+):/\1/ 使用\1 获取第一个捕获组。
  • 如果您只想将这些提取到一个新文件中(您的问题不清楚),您可以使用 sed 或 grep 更轻松地完成此操作; sed 示例:sed -n '/^\s*case\s\+/{s/\s*case\s\+\([^:]\+\):/\1/;p}' file
  • @beerbajay:是的,在新文件中没问题。我同意 sed 会做得很好,只是我必须启动命令提示符并再次找到该文件,所以我正在寻找 Vim 解决方案。
  • @mathematical.coffee:一点也不。问题不在于搜索和替换(除非您包含新行),而是抓取所有匹配项并将它们放入另一个缓冲区。
  • 这与这个问题非常相似:stackoverflow.com/questions/4503748/…

标签: regex vim match text-extraction


【解决方案1】:

有一种通用的方法可以收集整个作品中的模式匹配 的文本。该技术利用替代品 :substitute 命令的表达功能 (见:help sub-replace-\=)。关键思想是使用替换 枚举所有模式匹配以评估存储的表达式 无需更换。

首先,让我们考虑保存匹配项。为了保持顺序 匹配的文本片段,使用列表很方便 (见:help List)。但是,无法修改列表 直接使用:let 命令,因为没有办法 在表达式中运行 Ex 命令(包括 \= 替换表达式)。 然而,我们可以调用其中一个修改列表的函数。为了 例如,add() 函数旨在将给定项目附加到 指定列表(参见:help add())。

另一个问题是如何避免在运行时修改文本 一个替代品。一种方法是使模式始终具有 通过添加 \ze 或添加 \zs 原子来实现零宽度匹配 (见:help /\zs:help /\ze)。这样修改的图案 捕获在出现的之前或之后的空字符串 文本中的原始模式(这种匹配称为 零宽度 匹配 在 Vim 中;见:help /zero-width)。那么,如果替换文本也是 空,替换实际上没有任何改变:它只是替换 与空字符串匹配的零宽度。

由于add()函数,以及大部分列表修改 函数,返回对更改列表的引用,用于我们的技术 为了工作,我们需要以某种方式从中获取一个空字符串。最简单的 方法是通过指定范围从中提取零长度的子列表 索引,使得起始索引大于结束索引。

结合上述思路,我们得到如下Ex命令:

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

在它执行之后,第一个子组的所有匹配都被累加 在变量t引用的列表中,可以按原样使用或 以某种方式处理。例如,粘贴列表一的内容 在插入模式下在单独的行上按一个,键入

Ctrl+R=tEnter

要在普通模式下执行相同操作,只需使用:put 命令:

:pu=t

【讨论】:

  • 不错的答案。我特别喜欢替换表达式中extend() 的小技巧。
  • @HerbertSitz:谢谢,我刚刚注意到可以使用add() 函数代替extend()。顺便说一句,我已经重写了答案以更详细地解释该技术。
  • 不错的把戏。由于替换具有设置'修改'的副作用,无论如何,我们可以选择让add()返回最后添加的元素[-1];这使我们免于零宽度匹配和捕获::let t=[] | %s/\&lt;case\s\+\(\w\+\):/\=add(t,submatch(0))[-1]/g
  • @Ingo:但是我们最终会得到包含case Foo:case Bar: 等的列表,而不是FooBar 等。如果不使用\zs\ze 更改匹配边界,我们似乎无法正确解决问题。
【解决方案2】:

虽然不可能编写单行代码来完成您的示例,但很难以交互方式键入诸如 :%s/case \([^:]*\):/\=.../ 之类的命令。

我更喜欢通过以下步骤使用vim-grex

  1. 使用/ 检查正则表达式是否与预期的行匹配。 例如:/^\s*\&lt;case\s\+\([^:]*\):.*$&lt;Enter&gt;
  2. 执行:Grey。它会抽出与当前搜索模式匹配的行。
  3. :new 等打开一个新缓冲区。
  4. p 等添加抽出的行。
  5. 通过:%s//\1/修剪无趣的部分。

【讨论】:

    【解决方案3】:

    鉴于 'help' 可能是 'rust' 或 'perlang' 之类的任何单词,如何使用 vim 正则表达式从以下行中提取单词。

    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中的字符串并匹配它以断言行的开头,然后是任意数量的空格,文字vim和文字冒号,然后是任意数量的任意字符,后跟冒号@987654326 @ 与任何带有字母的单词,然后是任何东西,并断言该行以冒号结尾。将所有内容放入名为 1 的寄存器中,然后将其返回到参数 2 中,substitute 接受并替换之前的字符串。

    作为一般理念,任何比您的手指在屏幕上长的正则表达式都是巨大的失败,因此请降低屏幕分辨率直到适合为止。

    【讨论】:

      【解决方案4】:
      :g/^case\s\L\l\+\scase.*/s/case/\r&/g
      :let @a=''|g/^case\s\L\l\+:/y A
      

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

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

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

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

      【讨论】:

      • 第一个代码中列出的命令肯定有一些错误。您在发布之前运行过它们吗?这两个命令都不会运行!您的意思可能类似于:let@a=''|g/^case\s\L\l\+:/y A
      • :v/.../d:g!/.../d 是一个不错的技巧,因此它会删除所有不匹配的行。然而,它并没有真正严格正则表达式匹配的表达式。它正在提取匹配的行,然后假设每行有一个匹配,第二个搜索和替换将起作用。它在一般情况下不起作用。我会更新我的示例。
      • @ib。谢谢你指出,你是对的。当我在 Windows 上,在 excel 前面时会发生这种情况...更新 hte 答案。
      • @Wernight,好的,我已经针对您的特殊情况更新了答案。
      【解决方案5】:

      作为 ib. 接受的答案的一个小补充,效果很好。似乎标志n 足以避免不必要的替换问题。

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

      来自 s_flag 帮助:

      [n] 报告匹配数,不要实际替换。 [c] 标志被忽略。匹配报告为好像'report' 为零。 对计数项目很有用。 如果使用\= sub-replace-expression,表达式将为 每场比赛都在沙盒中进行评估。

      【讨论】:

        猜你喜欢
        • 2011-09-27
        • 2020-08-27
        • 1970-01-01
        • 1970-01-01
        • 2013-05-09
        • 1970-01-01
        • 2010-10-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多