可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Is there a way to insert the value from some sort of counter variable in gVim search/replace?
e.g. convert this document:
<SomeElement Id="F" ... />
<SomeElement Id="F" ... />
<SomeElement Id="F" ... />
to this resulting document:
<SomeElement Id="1" ... />
<SomeElement Id="2" ... />
<SomeElement Id="3" ... />
I imagine the command would look something like:
:%s/^\(\s*<SomeElement Id="\)F\(".*\)$/\1<insert-counter-here>\2/g
I am using a very recent Windows build, from their provided installer. I strongly prefer not to install any additional tools.
Also, I'd ideally like to avoid having to install scripts to support this, but I'm willing to if it is the only way to do it.
回答1:
Vim wiki instructions seems to be the easiest solution (at least for me).
Example below replaces all occurences of PATTERN
with REPLACE_[counter]
(REPLACE_1
, REPLACE_2
etc.):
:let i=1 | g/PATTERN/s//\='REPLACE_'.i/ | let i=i+1
To answer the question it might look like this:
:let i=1 | g/SomeElement Id="F"/s//\='SomeElement Id="'.i.'"'/ | let i=i+1
回答2:
It is possible to have an increasable counter using substitute with an
expression feature (see :help sub-replace-\=
). Since the \=
construct
allows only expressions, the :let
command is forbidden to use, and
therefore a variable could not be set the usual way. However, there is
a simple trick to change the value of a variable in expression if that
variable is a list or a dictionary. In that case, its contents could be
modified by the map()
function. In such a manner, substitution for the case
described in the question would look as follows.1
:let n=[0] | %s/Id="F"/\='Id="'.map(n,'v:val+1')[0].'"'/g
Or better,2
:let n=[0] | %s/Id="\zsF\ze"/\=map(n,'v:val+1')/g
This short one-liner completely solves the issue.
For frequent replacements such as the above one, one can define an auxiliary
function
function! Inc(x)
let a:x[0] += 1
return a:x[0]
endfunction
and make substitution commands even shorter,
:%s/Id="\zsF\ze"/\=Inc(n)/g
1 The tricky part here is in the substitute part of the
replacement. Since it starts with \=
the rest of it is interpreted as an
expression by Vim. Thus, 'Id="'.map(n, 'v:val+1').'"'
is an ordinary
expression. Here a string literal 'Id="'
is concatenated (using the .
operator) with return value of the function call map(n, 'v:val+1')
, and with
another string, '"'
. Function map
expects two arguments: a list (as in
this case) or a dictionary, and a string containing expression that should be
evaluated for each of the items in the given list or dictionary. Special
variable v:val
denotes an individual list item. So the 'v:val+1'
string
will be evaluated to a list item increased by one.
2 The \zs
and \ze
pattern atoms are used to set the start and
the end of the pattern to replace, respectively (see :help /\zs
and :help
/\ze
). That way the whole search part of the substitute command is matched,
but only the part between \zs
and \ze
is replaced. This avoids clumsy
concatenations in the substitute expression.
回答3:
Hmm this it little tricky one. Here is what I got so far. Try these 2 map command in a vim session:
:nmap %% :let X=1<cr>1G!!
:nmap !! /^\s*<SomeElement Id="F"<cr>:s/F"/\=X.'"'/<cr>:let X=X+1<cr>!!
Once that is there press %%
to start the fun part :)
It makes your given file as:
<SomeElement Id="1" ... />
<SomeElement Id="2" ... />
<SomeElement Id="3" ... />
<SomeElement Id="4" ... />
Explanation:
First nmap command is mapping following sequences to keystrokes %%
:
- initializing variable X to 1
- moving to start of first file
- calling another mapped keystroke
!!
Second nmap command is mapping following sequences to keystrokes !!
:
- Search for next occurrence of pattern
^\s*<SomeElement Id="F"
- If above pattern is found then search and replace
F"
by variable X
and a quote "
- increment the vim variable X by 1
- Recursively call itself by making a call to
!!
- Single dot
.
is used for concatenation of strings in vim, very similar to php
This recursive calls stop when pattern ^\s*<SomeElement Id="F"
is not found anymore in the file.
回答4:
Very simple solution. I've had to do this several times.
:let i=1 | g/^\(\s*<SomeElement Id="\)F\(".*\)$/s//\=submatch(1).i.submatch(2)/ | let i=i+1
Based off of the following tip. http://gdwarner.blogspot.com/2009/01/vim-search-and-replace-with-increment.html
回答5:
Put this in your vimrc or execute it in your current session:
function! Inc(x)
let a:x[0] += 1
return a:x[0]
endfunction
function IncReplace(pos, behind, ahead, rep)
let poss=a:pos-1
let n=[poss]
execute '%s/' . a:behind . '\zs' . a:rep . '\ze' . a:ahead . '/\=Inc(n)/g'
endfunction
Then execute :call IncReplace(1, 'Id="', '"', 'F')
The first argument is the number you want to start from, the second is what you want to match behind the number, the third is what you want to match ahead of the number and the fourth is what you actually want to replace.
回答6:
Maybe plugin increment.vim will help