微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何从VIM中的字符串列表中替换?

我是一个vim用户,我希望能够在替换时循环一系列子串.我如何使用一些vim魔法来从这样的一组行:
Afoo
Bfoo
Cfoo
Dfoo

Abar
Bbar
Cbaz
Dbaz

我想从头开始搜索我的文件,以便下一次出现foo,并用bar替换前两个实例,用baz替换后两个实例.使用for循环是最好的选择吗?如果是这样,那么如何在替换命令中使用循环变量?

我会使用一个具有状态的函数,并从%s调用函数.就像是:
" untested code
function! InitRotateSubst()
    let s:rs_idx = 0
endfunction

function! RotateSubst(list)
    let res = a:list[s:rs_idx]
    let s:rs_idx += 1
    if s:rs_idx == len(a:list)
        let s:rs_idx = 0
    endif
    return res
endfunction

并使用它们:

:call InitRotateSubst()
:%s/foo/\=RotateSubst(['bar','bar','baz','baz'])/

如果您愿意,可以将对这两个命令的调用封装到单个命令中.

编辑:这是一个集成为命令的版本:

>根据需要接受尽可能多的替换,所有替换都需要用分隔符分隔;
>支持反向引用;
>只能替换N个第一次出现,N ==如果命令调用被绑定指定的替换次数(带!)
>不支持像g,i(:h:s_flags)这样的通常标志 – 为此,我们可能会强制执行命令调用,最后总是以/(或任何分隔符)结束,如果不是最后一个文本被解释为标志.

这是命令定义:

:command! -bang -nargs=1 -range RotateSubstitute <line1>,<line2>call s:RotateSubstitute("<bang>",<f-args>)

function! s:RotateSubstitute(bang,repl_arg) range
  let do_loop = a:bang != "!"
  " echom "do_loop=".do_loop." -> ".a:bang
  " reset internal state
  let s:rs_idx = 0
  " obtain the separator character
  let sep = a:repl_arg[0]
  " obtain all fields in the initial command
  let fields = split(a:repl_arg,sep)

  " prepare all the backreferences
  let replacements = fields[1:]
  let max_back_ref = 0
  for r in replacements
    let s = substitute(r,'.\{-}\(\\\d\+\)','\1','g')
    " echo "s->".s
    let ls = split(s,'\\')
    for d in ls
      let br = matchstr(d,'\d\+')
      " echo '##'.(br+0).'##'.type(0) ." ~~ " . type(br+0)
      if !empty(br) && (0+br) > max_back_ref
    let max_back_ref = br
      endif
    endfor
  endfor
  " echo "max back-ref=".max_back_ref
  let sm = ''
  for i in range(0,max_back_ref)
    let sm .= ','. 'submatch('.i.')' 
    " call add(sm,)
  endfor

  " build the action to execute
  let action = '\=s:DoRotateSubst('.do_loop.',' . string(replacements) . sm .')'
  " prepare the :substitute command
  let args = [fields[0],action ]
  let cmd = a:firstline . ',' . a:lastline . 's' . sep . join(args,sep)
  " echom cmd
  " and run it
  exe cmd
endfunction

function! s:DoRotateSubst(do_loop,list,replaced,...)
  " echom string(a:000)
  if ! a:do_loop && s:rs_idx == len(a:list)
    return a:replaced
  else
    let res0 = a:list[s:rs_idx]
    let s:rs_idx += 1
    if a:do_loop && s:rs_idx == len(a:list)
        let s:rs_idx = 0
    endif

    let res = ''
    while strlen(res0)
      let ml = matchlist(res0,'\(.\{-}\)\(\\\d\+\)\(.*\)')
      let res .= ml[1]
      let ref = eval(substitute(ml[2],'\\\(\d\+\)','a:\1',''))
      let res .= ref
      let res0 = ml[3]
    endwhile

    return res
  endif
endfunction

可以这样使用:

:%rotateSubstitute#foo#bar#bar#baz#baz#

甚至,考虑到初始文本:

AfooZ
BfooE
CfooR
DfooT

命令

%rotateSubstitute/\(.\)foo\(.\)/\2bar\1/\1bar\2/

会产生:

Zbara
BbarE
RbarC
DbarT

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐