在Vim中使用递增值进行搜索和替换

28

假设我写了一个简单的 CSS 规则:

.star_10 {
  background: url(stars.png) no-repeat 0 0;
}

我需要得到10个,所以我复制了9次。

.star_10 {
  background: url(stars.png) no-repeat 0 0;
}
.star_10 {
  background: url(stars.png) no-repeat 0 0;
}
.star_10 {
  background: url(stars.png) no-repeat 0 0;
}
.star_10 {
  background: url(stars.png) no-repeat 0 0;
}
.star_10 {
  background: url(stars.png) no-repeat 0 0;
}

现在我想要改变 star_100 0 的值为递增的数值,使其看起来像这样:

.star_10 {
  background: url(stars.png) no-repeat 0 0;
}
.star_9 {
  background: url(stars.png) no-repeat 0 -18px;
}
.star_8 {
  background: url(stars.png) no-repeat 0 -36px;
}
.star_7 {
  background: url(stars.png) no-repeat 0 -54px;
}

那么我该如何查找/替换每个实例,执行计算并将其写入?


1
这是一个很好的起点。http://vim.wikia.com/wiki/VimTip971 - Node17
@Node17,哦,我用这个技巧在一个文件中重新编号了6000帧...效果非常好。 - ulidtko
关于@rene-nyffenegger的回答:https://dev59.com/Y2445IYBdhLWcg3wRoLH#4951590是否有一种方法可以在函数中放置参数(start_value,decrement_value)? - Kevin Lee
5个回答

61

您可以使用宏轻松完成。假设您只有以下内容:

.star_10 {
  background: url(stars.png) no-repeat 0 0;
}

将光标放置在第一个点(在 .star10 中)上,然后在正常模式下键入以下内容:

qa3yy3jp^Xjt;18^Xk0q

解释:

  1. qa 将在寄存器 "a" 中开始宏录制。
  2. 3yy 将复制接下来的 3 行。
  3. 3j 将把光标向下移动 3 行。
  4. p 将粘贴之前复制的文本。
  5. ^X (ctrl+x) 将星号等级数减少一。
  6. j 将把光标向下移动一行。
  7. t; 将把光标放置在当前行的下一个 ; 前。
  8. 18^X 将背景的 Y 坐标减少 18;
  9. k 将把光标向上移动一行,
  10. 0 将把光标放置在行的开头。
  11. q 将停止宏录制。

之后,你可能会得到类似这样的内容。

.star_10 {
  background: url(stars.png) no-repeat 0 0;
}

.star_9 {
  background: url(stars.png) no-repeat 0 -18;
}

就是这样。只需将光标放在 .star_9 上的点上,然后按下8@a以执行在寄存器a中记录的宏,再执行八次。


12
虽然很多人谈论函数、插件等,但宏是 Vim 的真正强项。 - Drasill
7
为什么 Vim 能够轻松胜过大多数“先进”的 IDE 的又一个绝佳例子。 - Steven Lu
1
非常高兴得到这个答案 - 这是我第一次听说Vim宏。 - Vivian
1
这个答案与提问者的数据非常有关系,他一遍又一遍地以相同的数字开始。我可能会问一个带有相同标题文本的问题,很可能会被关闭为重复问题,但是我的数据在每次匹配中都有不同的数字,只是具有相同的模式。很难猜测在^x的位置上要使用什么。 - hippietrail
1
不得不承认,当我看到“qa3yy3jp^Xjt;18^Xk0q”时,我笑了很多,然后我开始尝试并学到了不少东西。干杯。 - John Von Neumann
显示剩余3条评论

14
你可以使用带有 \= 符号的 s/pattern/replace 结构来评估一个函数或表达式,如下所示:
减少 .star_10
let g:decr = 11

fu! Decr() 
  let g:decr = g:decr - 1
  return g:decr
endfu

:%s/.star_10/\=".star_" . Decr()/

同样地,你可以这样做

let g:decr_18 = 18
fu! Decr18() 
  let g:decr_18 = g:decr_18 - 18
  return g:decr_18
endfu

然后用 0 0; 替换

:%s/no-repeat 0 0;/\="no-repeat 0 " . Decr18() . "px"/

这两个功能都声明了一个(全局)变量,在函数内进行操作并返回该变量。 函数本身针对与模式匹配的每一行调用。 模式将替换为\=后面的表达式。


1
有没有一种方法可以在函数中放置参数(start,decrement_value)来完成这个操作? - Kevin Lee

2

你可能可以为此编写一个函数,但我通常做得更简单。我使用涉及^A和^X键的关键宏(递增和递减数字)。使用qa开始录制,使用计数修饰符(例如18^X)使用这些键进行操作,然后仅执行10@即可。


1

仅使用纯vim资源,无脚本:

:let c=10 | g/_\zs\d\+\ze/ s//\=c/ | let c-=1 
:let c=0 | g/no-repeat 0 \zs\d\+/ s//\=c/ | let c-=18

0

我需要这个解决方案,但也需要支持前导零。我的使用方式更像示例1,而不是示例2(这是原始示例)。

我创建了一个名为SearchIncr()(搜索、替换和递增)的函数。

有四个必需参数:target_stringreplacement_stringstart_valueincrement_value。 还有两个可选参数:leading_zerosposition

leading_zeros是正整数值,position是“before”或“after”。参见示例2。


示例1

:'<,'>call SearchIncr("0 0","new",1,5,8)

如果文件看起来像这样:

what 0 0 post
areu 0 0 post
doin 0 0 post

然后您将得到:

what new00000001 post
areu new00000006 post
doin new00000011 post

示例 2

:'<,'>call SearchIncr("0;","px;",-18,-18,0,'after')

如果文件长这样:

.star_10 {
  background: url(stars.png) no-repeat 0 0;
}
.star_10 {
  background: url(stars.png) no-repeat 0 0;
}
.star_10 {
  background: url(stars.png) no-repeat 0 0;
}

你将会得到以下内容:
.star_10 {
  background: url(stars.png) no-repeat 0 -18px;
}
.star_10 {
  background: url(stars.png) no-repeat 0 -36px;
}
.star_10 {
  background: url(stars.png) no-repeat 0 -54px;
}

vimscript在这里:

" place functions here
function! SearchIncr(a1,a2,a3,a4,...) 
  " patn, repl, start, increment
  let target = a:a1
  let repl = a:a2
  let startval = a:a3
  let increment = a:a4

  " 1st optional argument handles leading zeros
  " process optional leading zero
  if a:0 >= 1
    let leadingz = a:1
  else
    " default to 0
    let leadingz = 0
  endif

  " 2nd optional argument handles if target string goes before or after
  " default is before
  if a:0 == 2
    " ideally set to 'after'
    let repl_pos = a:2 
  else
    let repl_pos = 'before'
  endif

  " inits
  let l:incr = startval - increment
  let lnum = a:firstline

  " cycle though selection
  while lnum <= a:lastline

    " the FORMATTING string
    if repl_pos == 'before'
      let l:incrstring = '\=printf(''%s%0'. leadingz . 'd'',repl,l:incr)'
    elseif repl_pos == 'after'
      let l:incrstring = '\=printf(''%0'. leadingz . 'd%s'',l:incr,repl)'
    else
      "default to no leading zero and before placement
      let l:incrstring = '\=printf(''%s%0d'',repl,l:incr)'
    endif

    " only increment counter when target matches
    if match(getline(lnum),target) > 0
      let l:incr = l:incr + increment
    endif

    " the search and replace
    call setline(lnum,substitute(getline(lnum),target,
    \ l:incrstring,'g'))

    let lnum = lnum + 1
  endwhile
endfunction

如果有任何意见可以改进上面的内容,将不胜感激!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接