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

idb*_*rii 15 regex vim

我的具体案例是一个包含大量文本和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}"
Run Code Online (Sandbox Code Playgroud)

从vim调用grep可能需要调整我的正则表达式(例如:remove\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
Run Code Online (Sandbox Code Playgroud)

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()
Run Code Online (Sandbox Code Playgroud)

Pet*_*ker 11

这种效果可以通过使用子替换特殊替换和setreg()线性来实现

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

或者全部在一行中:

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

概述:使用替换将每个匹配按顺序附加到寄存器"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
  • 使用最后一个搜索模式.虽然您可以使用您想要的任何模式修改substitute命令:%s/<pattern>/\=setreg('A', submatch(0), 'l')/g

获得更多帮助

:h :s\=
:h :let-@
:h submatch()
:h setreg()
:h :d
:h :p
Run Code Online (Sandbox Code Playgroud)


ice*_*ime 6

假设<ip>你的正则表达式匹配一个IP地址,我猜你可以这样做:

:%s/.\{-}\(<ip>\).*/\1/g
Run Code Online (Sandbox Code Playgroud)

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


Ben*_*son 6

简而言之,我正在寻找一种方法来做到这一点,而无需离开 vim

足够简单:

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

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


ZyX*_*ZyX 5

:set nowrapscan
:let @a=""
gg0qac/\v(\d{1,3}\.){3}\d{1,3}<CR><CR><Esc>//e+1<CR>@aq@adG
Run Code Online (Sandbox Code Playgroud)

说明:

  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:删除到文件末尾.