【问题标题】:What is the easiest way to swap occurrences of two strings in Vim?在 Vim 中交换两个字符串的最简单方法是什么?
【发布时间】:2025-12-09 12:15:01
【问题描述】:

string_b 替换所有出现的string_a,同时将已经存在的string_b 更改为string_a 的最简单方法是什么?我目前的方法如下:

:s/string_a/string_c/g  
:s/string_b/string_a/g  
:s/string_c/string_b/g  

虽然这可行,但它需要额外的输入并且似乎效率低下。有人知道更好的方法吗?

【问题讨论】:

  • 您可能(也)想在 SuperUser.com 上尝试这个问题。
  • 这是一个很好的问题——人们会认为这很容易......我想可以编写一个接受两个参数并为你完成三个步骤的函数,但我也希望找到这样的具有快速网络搜索的功能。
  • 如果您的文件已经在任何地方包含“string_c”,则此方法将失败。如果您是人类并且可以选择一个您知道不在文件中的单词,那很好,但是教一个函数来猜测一个好的中间词会更加困难。最好一次性完成。
  • @BrianCarper 我认为即使 string_c 没有出现在文本中,它也会失败,例如如果文本是字母“abcde...”,则 string_a 为“bcd”,string_c 为“aa”。
  • As Peter Rincker answered on another questionAbolish 插件现在也能很好地做到这一点!

标签: vim scripting vi


【解决方案1】:

我会这样做:

:%s/\v(foo|bar)/\={'foo':'bar','bar':'foo'}[submatch(0)]/g

但是打字太多了,所以我会这样做:

function! Mirror(dict)
    for [key, value] in items(a:dict)
        let a:dict[value] = key
    endfor
    return a:dict
endfunction

function! S(number)
    return submatch(a:number)
endfunction

:%s/\v(foo|bar)/\=Mirror({'foo':'bar'})[S(0)]/g

但这仍然需要输入两次foobar,所以我会这样做:

function! SwapWords(dict, ...)
    let words = keys(a:dict) + values(a:dict)
    let words = map(words, 'escape(v:val, "|")')
    if(a:0 == 1)
        let delimiter = a:1
    else
        let delimiter = '/'
    endif
    let pattern = '\v(' . join(words, '|') . ')'
    exe '%s' . delimiter . pattern . delimiter
        \ . '\=' . string(Mirror(a:dict)) . '[S(0)]'
        \ . delimiter . 'g'
endfunction

:call SwapWords({'foo':'bar'})

如果您的某个单词包含/,则必须传入一个您知道您的单词都不包含的分隔符,例如

:call SwapWords({'foo/bar':'foo/baz'}, '@')

这还具有能够一次交换多对单词的好处。

:call SwapWords({'foo':'bar', 'baz':'quux'})

【讨论】:

  • 这太棒了!我唯一的问题是它会在任何地方交换事件。有没有办法传递您希望发生交换的行,类似于 :17,34s/foo/bar/g 仅在第 17 到 34 行替换?
  • @dsg 可以通过对 SwapWords() 进行两次更改来完成。将关键字range 放在第一行的...) 之后,然后将末尾附近的'%s' 替换为a:firstline . ',' . a:lastline . 's'。然后它将采用与:s 完全相同的范围。
【解决方案2】:

您可以使用 Tim Pope 的 Abolish 插件轻松做到这一点

:%S/{transmit,receive}/{receive,transmit}

【讨论】:

  • %S /{,}/{,}/g 当我尝试交换整个单词时,它不起作用。我该怎么办?
  • @JanNetherdrake 如果您想使用整个单词,请使用 Subvert 的 w 标志。 :%S/{test,foo}/{foo,test}/gw。请参阅:h abolish-search 了解更多信息。
【解决方案3】:

这是我如何交换两个词 skiplimit

%s/skip/xxxxx/g | %s/limit/skip/g | %s/xxxxx/limit/g

很确定有人可以把它变成一个更短的命令,接受两个参数。

【讨论】:

    【解决方案4】:

    swapstrings plugin为此提供了一个方便的命令:

    :SwapStrings string_a string_b
    

    【讨论】:

      【解决方案5】:

      您可以使用单个命令来完成,如下面的代码所示:

      :%s/\<\(string_a\|string_b\)\>/\=strpart("string_bstring_a", 8 * ("string_b" == submatch(0)), 8)/g
      

      【讨论】: