如何从 VIM 中的字符串列表进行替换? [英] How do I substitute from a list of strings in VIM?

查看:22
本文介绍了如何从 VIM 中的字符串列表进行替换?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 vim 用户,我希望在替换时能够遍历一系列子字符串.我怎样才能使用一些 vim 魔法从这样的一组行中走出来:

I am a vim user, and I want to be able to loop over a range of substrings when I am substituting. How can I use some vim magic to go from a set of lines like this:

Afoo
Bfoo
Cfoo
Dfoo

Abar
Bbar
Cbaz
Dbaz

?

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

I want to search my file from the start for the next occurance of foo, and replace the first two instances with bar, and the second two with baz. Is using a for loop the best option? If so, then how do I use the loop variable in the substitution command?

推荐答案

我会使用一个有状态的函数,并从 %s 调用这个函数.类似的东西:

I would use a function that has a state, and call this function from %s. Something like:

" 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'])/

如果您愿意,可以将这两个命令的调用封装为一个命令.

The call to the two commands could be encapsulated into a single command if you wish.

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

Here is a version integrated as a command that:

  • 接受任意数量的替换,所有替换都需要用分隔符分隔;
  • 支持反向引用;
  • 只能替换第一次出现的 N,N == 指定的替换次数,如果命令调用被破坏(用 !)
  • 不支持像 g, i (:h :s_flags) 这样的常用标志——为此,我们必须例如将命令调用强加到总是以/结尾(或其他任何分隔符),如果不是最后一个文本被解释为标志.
  • accepts as many replacements as we wish, all the replacements needs to be separated with the separator-character ;
  • supports back-references ;
  • can replace only the N first occurrences, N == the number of replacements specified if the command call is banged (with a !)
  • does not support usual flags like g, i (:h :s_flags) -- for that, we would have for instance to impose the command call to always ends up with a / (or whatever separator-character), if not the last text is interpreted as 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

这篇关于如何从 VIM 中的字符串列表进行替换?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆