删除 Vim 中除正则表达式匹配之外的所有内容

问题描述 投票:0回答:4

我的具体案例是一个包含大量文本和 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 可能需要调整我的正则表达式(例如:删除)。使用 vim 的增量搜索显示我的模式是正确的,并且我不想在 grep 中验证我的正则表达式。


编辑:感谢Peter,这是我现在使用的功能。 (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()

Edit3:该代码现在位于插件中:vim-searchsavvy提供了

:ClearAllButMatches
命令。

regex vim
4个回答
14
投票

这种效果可以通过使用 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/<pattern>/\=setreg('A', submatch(0), 'l')/g

寻求更多帮助

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

8
投票

假设

<ip>
是你匹配 IP 地址的正则表达式,我想你可以这样做:

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

其中

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


6
投票

简而言之,我正在寻找一种无需离开 vim 即可完成此操作的方法

很简单:

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

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


6
投票
: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}<CR>
    :改变直到图案。
  6. c{motion}<CR><ESC>
    :用换行符替换文本(这里
    {motion}
    /{pat}<CR>
    )。
  7. //e+1<CR>
    :搜索最后一个模式,将一个字符移到末尾(换行符周围,但如果你的行看起来像这样:
    IP<newline>IP
    ,可能会有问题)。
  8. @a
    :执行
    @a
    宏(录制时为空,录制完成后会重复步骤1-7,直到出错)。
  9. q
    :结束录制
    @a
  10. @a
    :执行
    @a
    宏。
  11. dG
    :删除到文件末尾。
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.