【问题标题】:In Vim how can I search and replace every other match?在 Vim 中,我如何搜索和替换所有其他匹配项?
【发布时间】:2018-10-24 21:33:37
【问题描述】:

假设我有以下文件

<block>
    <foo val="bar"/>
    <foo val="bar"/>
</block>
<block>
    <foo val="bar"/>
    <foo val="bar"/>
</block>

我怎么能把它变成

<block>
    <foo val="bar1"/>
    <foo val="bar"/>
</block>
<block>
    <foo val="bar1"/>
    <foo val="bar"/>
</block>

我尝试做的一件事是用:%s/bar/bar1/gc 录制一个宏,然后分别按yn 一次,然后尝试编辑该宏。由于某种原因,我无法编辑宏。 :(

【问题讨论】:

    标签: vim editor replace


    【解决方案1】:

    只是为了表明这可以通过替换来完成:

    :let a = ['', '1']
    :%s/bar\zs/\=reverse(a)[0]/g
    

    概述

    在每次替换后数组就地反转后,用变量a中数组的第一个元素替换每个bar的末尾。

    细节的荣耀

    1. let a = ['', '1'] 定义一个变量 a 来保存我们的数组
    2. %s/.../.../ 对文件中的每一行进行替换
    3. %s/bar\zs/.../ 在 bar 上进行替换,但在 bar 之后使用 \zs 开始替换
    4. :s 命令的替换部分内的\= 使用以下表达式的值
    5. reverse(a) reverse 只是简单地反转数组,但在原地进行
    6. reverse(a)[0] reverse 返回现在反转的数组所以获取第一个元素
    7. /g 替换行中的所有出现(可选)

    一般情况

    :let a = ['a', 'b', 'c']
    :%s/bar\zs/\=add(a, remove(a, 0))[-1]/g
    

    一般情况下“旋转”数组a,并使用数组的最后一个位置作为替换替换的值。

    有关更多帮助,请参阅

    :h :s
    :h range
    :h /\zs
    :h :s\=
    :h reverse(
    :h :s_flags
    :h Lists
    :h add(
    :h remove
    

    【讨论】:

    • 这是 Vim power 最好的含义之一。
    • 这简直太邪恶了。
    • 这只是为我节省了 20 分钟手动检查 385+ x 2 行的时间。如果我能给你更多的分数,我会的。
    【解决方案2】:

    你可以使用

    :%s/bar/bar1/gc
    

    它会在每场比赛中询问你是否要替换它。

    否则,您必须在其中指定全部内容,然后将第一个 bar 替换为 bar1。

    【讨论】:

      【解决方案3】:

      我会用宏来做:

      qv            start recording in register v
      /"bar"/e<cr>  search for "bar" and position the cursor at the end of the match
      i1<esc>       insert 1 before the cursor and go back to normal mode
      n             jump to next match
      q             stop recording
      

      之后,执行{count}@v

      【讨论】:

      • 暂时设置nowrapscan 以防它开启可能是个好主意。
      【解决方案4】:

      试试这个:

      :%s/bar\(.*\)\n\(.*\)bar/bar1\1\r\2bar
      

      【讨论】:

      • 但是我们能不能让它更通用,而不仅仅是匹配连续的行。 (我已经给你投了赞成票:)这确实解决了眼前的问题)
      • @OsadaLakmal 我也在考虑同样的事情,但我不知道该怎么做。我试过%s/bar\(\_.*\)bar/bar1\1bar 但它不起作用可能是因为贪婪的东西。多行正则表达式对我来说有点复杂,也许有更多经验的人可以想出它..
      【解决方案5】:
      :let dosubs=1
      :%s/bar/\=[dosubs?'bar1':submatch(0),extend(g:,{'dosubs':!dosubs})][0]/g
      

      【讨论】:

        【解决方案6】:

        这是一个可以解决问题的自定义命令。它使用 replace 表达式 来计算完成的替换,并使用传递的附加参数来决定是否应该进行替换。 (这允许比每秒更复杂的安排。)您的示例将很简单:

        :%SubstituteSelected/\<bar\>/&1/ yn
        

        这是(不幸的是相当长)实现:

        ":[range]SubstituteSelected/{pattern}/{string}/[flags] {answers}
        "           Replace matches of {pattern} in the current line /
        "           [range] with {string}, determining whether a particular
        "           match should be replaced on the sequence of "y" or "n"
        "           in {answers}. I.e. with "ynn", the first match is
        "           replaced, the second and third are not, the fourth is
        "           again replaced, ...
        "           Handles & and \0, \1 .. \9 in {string}.
        function! CountedReplace()
            let l:index = s:SubstituteSelected.count % len(s:SubstituteSelected.answers)
            let s:SubstituteSelected.count += 1
        
            if s:SubstituteSelected.answers[l:index] ==# 'y'
                if s:SubstituteSelected.replacement =~# '^\\='
                    " Handle sub-replace-special.
                    return eval(s:SubstituteSelected.replacement[2:])
                else
                    " Handle & and \0, \1 .. \9 (but not \u, \U, \n, etc.)
                    let l:replacement = s:SubstituteSelected.replacement
                    for l:submatch in range(0, 9)
                        let l:replacement = substitute(l:replacement,
                        \   '\%(\%(^\|[^\\]\)\%(\\\\\)*\\\)\@<!' .
                        \       (l:submatch == 0 ?
                        \           '\%(&\|\\'.l:submatch.'\)' :
                        \           '\\' . l:submatch
                        \       ),
                        \   submatch(l:submatch), 'g'
                        \)
                    endfor
                    return l:replacement
                endif
            elseif s:SubstituteSelected.answers[l:index] ==# 'n'
                return submatch(0)
            else
                throw 'ASSERT: Invalid answer: ' . string(s:SubstituteSelected.answers[l:index])
            endif
        endfunction
        function! s:SubstituteSelected( range, arguments )
            let l:matches = matchlist(a:arguments, '^\(\i\@!\S\)\(.*\)\%(\%(^\|[^\\]\)\%(\\\\\)*\\\)\@<!\1\(.*\)\%(\%(^\|[^\\]\)\%(\\\\\)*\\\)\@<!\1\(\S*\)\s\+\([yn]\+\)$')
            if empty(l:matches)
                echoerr 'Invalid arguments'
                return
            endif
            let s:SubstituteSelected = {'count': 0}
            let [l:separator, l:pattern, s:SubstituteSelected.replacement, l:flags, s:SubstituteSelected.answers] = l:matches[1:5]
        
            execute printf('%ssubstitute %s%s%s\=CountedReplace()%s%s',
            \   a:range, l:separator, l:pattern, l:separator, l:separator, l:flags
            \)
        endfunction
        command! -bar -range -nargs=1 SubstituteSelected call <SID>SubstituteSelected('<line1>,<line2>', <q-args>)
        

        编辑

        我现在已将此(连同相关命令)发布为PatternsOnText plugin

        【讨论】:

          【解决方案7】:

          我找到了一个更简单的解决方案:

          :g/<block>/norm! j02f"i1
          
          :g ............ global command
          /<block>/ ..... every line with <block>
          j0 ............ goes down one line and to the column 1
          2f" ........... jumps to the second "
          i1 ............ insert the number one
          

          我以前的复杂解决方案

          \v%(block>\_{-})\zsbar
          %s,,&1,g
          
          
          
             \v ............ very magic (avoid lots of scapes)
             %  ............ ignore whats flows
             (  ............ starts (ignored) group
             \_ ............ multiline search
             .{-} .......... non-greedy 
             \zs ........... start pattern for substituition
             bar  .......... pattern we want to change 
          
             % ............. whole file
             s ............. substituition
             ,, ............ use last search (could be //)
             & ............. use searched pattern 
             1 ............. add 1 after it  
          

          【讨论】:

            猜你喜欢
            • 2011-06-15
            • 2017-09-21
            • 1970-01-01
            • 1970-01-01
            • 2011-11-09
            • 1970-01-01
            • 2010-10-05
            • 2015-02-15
            • 2021-04-16
            相关资源
            最近更新 更多