Reputation: 659
I have a large file containing a selection with jumbled numbers. It's supposed to be a seqeunce 1, 2, 3, ... but a few lines got screwed up. I would like to change something like
foo
bar
1 foobar
12345 foobar
6546458 foobar
4 foobar
to
foo
bar
1 foobar
2 foobar
3 foobar
4 foobar
I know I can use something like 3,$
to select the lines I care about and put = range(1,1000)
to make new lines starting with the numbers I want, but I want to put these numbers on lines that currently have data, not new lines. The jumbled numbers are several characters long, but always one word. Thanks.
Upvotes: 1
Views: 1953
Reputation: 89221
/^\d\+\s -- Searches for the first occurrence
ciw0<Esc> -- Replaces the word under cursor with "0"
yiw -- Copies it
:g//norm viwp^Ayiw
-- For each line that matches the last search pattern,
-- Replace the current word with copied text,
-- Increment it,
-- Copy the new value.
(<Esc>
is just Esc. ^A
is entered as Ctrl+V, Ctrl+A)
ciw
-- Change inner word. (:help c
):g
-- Global search. (:help :g
)viw
-- Select inner word. (:help v
)p
-- Paste (replacing selection) (:help v_p
)^A
-- Increment. (:help CTRL-A
)yiw
-- Yank inner word. (:help y
)Upvotes: 1
Reputation: 45147
Do the following:
:let i=1
:g/^\d\+/s//\=i/|let i=i+1
Setup some variable (let i=1
) to use as our counter. On every line that starts with a number (:g/^\d\+/
) we execute a substitution (:s//\=i/
) to replace the pattern with our counter (\=i
) and then increment our counter (let i=i+1
).
:g
at all? Why not just :s
?You could do this with just a substitution command however the sub-replace-expression, \=
, needs an expression to evaluate to a value (see :h sub-replace-expression
). As let i = i + 1
is a statement it will not be useful.
There are a few ways to overcome this issue:
map(arr, 'v:val+1')[0]
:g
trick from aboveFull example using in-place array modification:
:let i=[1]
:%s/^\d\+/\=map(i,'v:val+1')[0]
Personally, I would use whatever method you can remember.
:h :s
:h sub-replace-expression
:h :g
:h :let
:h expr
:h map(
:h v:val
Upvotes: 4
Reputation: 36272
You can use the following function:
function Replace()
let n = 1
for i in range(0, line('$'))
if match(getline(i), '\v^\d+\s') > -1
execute i . 's/\v^\d+/\=n/'
let n = n + 1
endif
endfor
endfunction
It traverses the whole file, check if each line begins with a number followed by a space character and does the substitution with the counter that increments with each change.
Call it like:
:call Replace()
That in your example yields:
foo
bar
1 foobar
2 foobar
3 foobar
4 foobar
Upvotes: 0