我是一个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