【问题标题】:Remove everything except regex match in Vim在 Vim 中删除除正则表达式匹配之外的所有内容
【发布时间】:2010-12-21 20:46:17
【问题描述】:

我的具体案例是一个包含大量文本和 IPv4 地址的文本文档。我想删除除 IP 地址之外的所有内容。

我可以使用:vglobal搜索([0-9]{1,3}\.){3}[0-9]{1,3}并删除所有没有IP地址的行,但之后我只知道如何搜索整行并选择匹配的文本。有没有更简单的方法。

简而言之,我正在寻找一种不使用外部程序(如 grep)来执行以下操作的方法:

grep --extended-regexp --only-matching --regexp="([0-9]{1,3}\.){3}[0-9]{1,3}"

从 vim 调用 grep 可能需要调整我的正则表达式(例如:删除 \v)。使用 vim 的增量搜索表明我的模式是正确的,我也不想在 grep 中验证我的正则表达式。


编辑:感谢彼得,这是我现在使用的功能。 (C 是我通常在我的函数中破坏的寄存器。)

"" Remove all text except what matches the current search result
"" The opposite of :%s///g (which clears all instances of the current search).
function! ClearAllButMatches()
    let old = @c
    let @c=""
    %s//\=setreg('C', submatch(0), 'l')/g
    %d _
    put c
    0d _
    let @c = old
endfunction

Edit2:我将其设为接受范围的命令(但默认为整个文件)。

"" Remove all text except what matches the current search result. Will put each
"" match on its own line. This is the opposite of :%s///g (which clears all
"" instances of the current search).
function! s:ClearAllButMatches() range
    let is_whole_file = a:firstline == 1 && a:lastline == line('$')

    let old_c = @c

    let @c=""
    exec a:firstline .','. a:lastline .'sub//\=setreg("C", submatch(0), "l")/g'
    exec a:firstline .','. a:lastline .'delete _'
    put! c

    "" I actually want the above to replace the whole selection with c, but I'll
    "" settle for removing the blank line that's left when deleting the file
    "" contents.
    if is_whole_file
        $delete _
    endif

    let @c = old_c
endfunction
command! -range=% ClearAllButMatches <line1>,<line2>call s:ClearAllButMatches()

【问题讨论】:

标签: regex vim


【解决方案1】:

这个效果可以通过使用 sub-replace-special 替换和setreg() linewise 来实现

:let @a=""
:%s//\=setreg('A', submatch(0), 'l')/g
:%d _
:pu a
:0d _

或全部在一行中:

:let @a=""|%s//\=setreg('A', submatch(0), 'l')/g|%d _|pu a|0d _

概述:使用替换将每个匹配项逐行附加到寄存器“a”中,然后用寄存器“a”的内容替换整个缓冲区

解释:

  1. let @a="" 清空我们将附加到的“a”寄存器
  2. %s//\=setreg('A', submatch(0), 'l')/g 使用最后一个模式全局替换
  3. \=expr 将用表达式的内容替换模式
  4. submatch(0) 获取刚刚匹配的整个字符串
  5. setreg('A', submatch(0), 'l') 追加(注意:大写“a”)到 @a 匹配的字符串,但逐行
  6. %d _删除黑洞寄存器中的每一行(又名@_)
  7. pu a将@a的内容放入缓冲区
  8. 0d _删除第一行

关注点:

  • 这将删除您的一个寄存器。这个例子破坏了@a
  • 使用最后一个搜索模式。尽管您可以使用任何您想要的模式修改替换命令:%s/&lt;pattern&gt;/\=setreg('A', submatch(0), 'l')/g

更多帮助

:h :s\=
:h :let-@
:h submatch()
:h setreg()
:h :d
:h :p

【讨论】:

  • 这是一个很棒的宏。谢谢。
【解决方案2】:

假设 &lt;ip&gt; 是您匹配 IP 地址的正则表达式,我想您可以执行以下操作:

:%s/.\{-}\(<ip>\).*/\1/g

其中\1 是第一个匹配的组(仅是地址),.\{-} 用于非贪婪匹配。

【讨论】:

  • 就像我说的,我希望避免搜索整行和匹配的文本——主要是因为打字很尴尬。可以使用非常神奇的正则表达式 (\v) 来缩短您的解决方案::%s/\v.{-}(&lt;ip&gt;).*/\1/。此外,它需要 vglobal 步骤。
  • 你为什么不直接定义一个同时执行 vglobal 步骤和 icecrime 步骤的函数,然后定义一些键映射来调用该函数并粘贴在你的 .vimrc 中?一次按键,你就完成了……一个键打字有多尴尬?
  • 如果每行有多个IP地址,它也不起作用。 (只需要第一个。)
  • 我使用了一个稍微修改过的版本::%s/.\{-}\(&lt;ip&gt;\)/\1 /g 来删除文件中除了&lt;ip&gt; 匹配的所有内容之外的所有内容,并将所有匹配内容用一个空格分隔。我能找到的唯一缺点是“尾部”(即文件末尾的不匹配文本)之后仍然存在,但这很容易删除,并且可能由比我有更好的 VIM 知识的人自动删除;-)
【解决方案3】:

简而言之,我正在寻找一种不离开 vim 的方法

很简单:

:1,$! grep --extended-regexp --only-matching --regexp="([0-9]{1,3}\.){3}[0-9]{1,3}"

(虽然我实际上投票赞成icecrime的替代答案)

【讨论】:

  • 这就是我离开 vim 的意思。
  • 这基本上是我目前正在尝试改进的解决方案:cabbrev selectgrep !egrep -oe""&lt;Left&gt;&lt;BS&gt;
  • 能否告诉我一些提示以了解更多关于命令第一部分的信息:1,$ 在感叹号之前?
  • @JikkuJose:只是:line command 或:line,line command 的范围。行可以是一个数字,一个$ 代表结束,或者像/hello/ 这样的模式。如果vim 手册页不够,sed 的手册页将为您提供大量信息。
【解决方案4】:
:set nowrapscan
:let @a=""
gg0qac/\v(\d{1,3}\.){3}\d{1,3}<CR><CR><Esc>//e+1<CR>@aq@adG

解释:

  1. set nowrapscan 禁用查找“文件末尾之后”的功能。
  2. let @a="":清空寄存器。
  3. gg0:转到第一行 (gg) 的第一列 (0)。
  4. qa:开始写宏。
  5. c/{pattern}&lt;CR&gt;:更改直到模式。
  6. c{motion}&lt;CR&gt;&lt;ESC&gt;:用换行符替换文本(这里{motion} 是/{pat}&lt;CR&gt;)。
  7. //e+1&lt;CR&gt;:搜索最后一个模式,在其末尾左移一个字符(换行,但如果您的行看起来像这样:IP&lt;newline&gt;IP,可能会有问题)。
  8. @a:执行@a宏(录制时为空,但录制完成后会重复步骤1-7直到出错)。
  9. q:结束录音@a。
  10. @a:执行@a 宏。
  11. dG:删除到文件末尾。

【讨论】:

  • 很好,这个自我重复的宏是我一直在思考的东西。
  • 我完全忘记了nowrapscan。在搜索中使用自重复宏的非常流畅的方式。干杯!
  • 如何从映射或函数中使用这个答案? gg0qac... 行在传递给正常时不会以相同的方式工作。似乎永远不会发送结束录制的 q 。同样,使用递归宏删除正斜杠的更简单的普通命令也不起作用:normal qaqqaf/x@aq 并使用后半部分作为映射给出相同的结果。
猜你喜欢
  • 2018-05-26
  • 1970-01-01
  • 2023-03-10
  • 2021-05-26
  • 2022-08-21
  • 2019-11-12
  • 2023-03-08
相关资源
最近更新 更多