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

如何从VIM中的字符串列表中进行替换?,vim,substitution,Vim,Substitution,我是vim用户,我希望在替换时能够循环一系列子字符串。我如何使用一些vim魔法从一组这样的行中走出来: Afoo Bfoo Cfoo Dfoo 到 ? 我想从一开始就搜索我的文件,查找下一次出现的foo,并将前两个实例替换为bar,将后两个实例替换为baz。使用for循环是最好的选择吗?如果是,那么如何在替换命令中使用循环变量 录制一个可以替换前两个宏的宏,然后使用:s替换其余宏可能会容易得多 宏可能看起来像/foo^Mcwbar^[。如果您不熟悉宏模式,只需点击q,a(存储宏的寄存器),然后

我是vim用户,我希望在替换时能够循环一系列子字符串。我如何使用一些vim魔法从一组这样的行中走出来:

Afoo
Bfoo
Cfoo
Dfoo

?


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

录制一个可以替换前两个宏的宏,然后使用:s替换其余宏可能会容易得多

宏可能看起来像
/foo^Mcwbar^[
。如果您不熟悉宏模式,只需点击
q
a
(存储宏的寄存器),然后按键
/foo cwbar


现在,一旦你有了宏,做
2@a
替换当前缓冲区中的前两个匹配项,并正常使用
:s
替换其余的。

这就是我尝试使用该宏的方式

qa          Records macro in buffer a
/foo<CR>    Search for the next instance of 'foo'
3s          Change the next three characters
bar         To the word bar
<Esc>       Back to command mode.
n           Get the next instance of foo
.           Repeat last command
n           Get the next instance of foo
3s          Change next three letters
baz         To the word bar
<Esc>       Back to command mode.
.           Repeat last command
q           Stop recording.
1000@a      Do a many times.
qa记录缓冲区a中的宏
/foo搜索“foo”的下一个实例
3s更改接下来的三个字符
酒吧到酒吧这个词
返回命令模式。
n获取foo的下一个实例
.重复最后一个命令
n获取foo的下一个实例
3.换下三个字母
baz到单词栏
返回命令模式。
.重复最后一个命令
停止录音。
1000@a做很多次。
任何关于如何做得更好的建议都是欢迎的

谢谢,
Martin。

我将使用具有状态的函数,并从%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
甚至,考虑到最初的文本:

AfooZ
BfooE
CfooR
DfooT
命令

%RotateSubstitute/\(.\)foo\(.\)/\2bar\1/\1bar\2/
将产生:

ZbarA
BbarE
RbarC
DbarT

严格来说,这不是您想要的,但对于循环来说可能很有用

我写了一个插件swapit,它可以帮助循环浏览字符串列表

 :Swaplist foobar foo bar baz
然后键入

 This line is a foo
创建一个简单的拉伸/粘贴行,转到最后一个单词并按ctrl-a切换

 qqyyp$^A
然后执行交换模式

 100@q
得到

This line is foo
This line is bar
This line is baz
This line is foo
This line is bar
This line is baz
This line is foo
This line is bar
This line is baz
This line is foo
This line is bar
This line is baz
...
它可能适用于您的问题,尽管它对{cword}敏感。

为什么不:

:%s/\(.\{-}\)foo\(\_.\{-}\)foo\(\_.\{-}\)foo\(\_.\{-}\)foo/\1bar\2bar\3baz\4baz/

我不确定它是否涵盖了问题的广度,但它确实具有作为一个简单替代品的优点。如果这个解决方案不涵盖,则更复杂的解决方案可能涵盖了问题的解决方案。

嘿,德里克,我只想说我昨天在vimeo上看了一些你的vim视频。我只想说我喜欢看它们,并学到了一些东西。你的如果我能更好地使用regex,解决方法就是我想要的。感谢这个简单的解决方案。看起来你在你的最后一个
之前缺少了一个
n
This line is foo
This line is bar
This line is baz
This line is foo
This line is bar
This line is baz
This line is foo
This line is bar
This line is baz
This line is foo
This line is bar
This line is baz
...
:%s/\(.\{-}\)foo\(\_.\{-}\)foo\(\_.\{-}\)foo\(\_.\{-}\)foo/\1bar\2bar\3baz\4baz/