8

I have a file which content like this:

id      phone   name
x'1234' 12345   jack
x'4567' 45678   Jojo
x'7890'  89456  Dio
x'4591'  34872  joseph

and i want to parse it into like this:

id  phone   name
1   12345   jack
2   45678   Jojo
3    89456  Dio
4    34872  joseph

I know basic regular expression could replace all id to any string like this:

:%s/x'\(\w\+\)'/1/g

and it will become:

id  phone   name
1   12345   jack
1   45678   Jojo
1    89456  Dio
1    34872  joseph

How to replace id to an increment variable ?

mpromonet
  • 11,326
  • 43
  • 62
  • 91
a4fz067lu
  • 189
  • 3
  • 7

5 Answers5

11

Mind that you can use an expression as the replacement string in the substitute command (:s). When the replacement string starts with \= it is evaluated as an expression.

Here, one possible solution is

:let i=1 | g/^x'\d\+'/s//\=i/ | let i=i+1

It finds all occurrences (one per line) of the ^x'\d\+' pattern and replaces it with the value if i that is incremented each time the match is found. As has been noted in comments, the | is a part of the g replacing "code", as "| is used to execute more than one command at a time".

Another solution is using the line() command (taking into account that your file has a header top line, so you should actually subtract 1 from the value returned with line()):

%s/^x'\d\+'/\=line('.')-1/

The ^x'\d\+' regex matches

  • ^ - start of a line
  • x' - x' string
  • \d\+ - 1+ digits
  • ' - a ' char.

There are other interesting "increment number in regex" examples at the Using an expression in substitute command page:

  • Number all the lines in a file (insert line number followed by a tab):
    :%s/^/\=line('.')."\t"/
  • Number a range of lines (from line 10 to line 20):
    :10,20s/^/\=line('.')."\t"/
  • Number a range of lines sequentially starting from 1:
    :let counter=0|10,20g//let counter=counter+1|s/^/\=counter."\t"
  • Number all the paragraphs in range starting from 1 (assuming the paragraphs are separated by one or more blank lines):
    :let counter=0|1,20g/^$\n^\s*[^\s]/let counter=counter+1|+1s/^/\=counter."\t"
    Note: The above command does not work for the first paragraph in the file if there is no blank line above it.
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • 1
    This is a very nice answer. But the part of it that's really interesting is that `| let i=i+1` is part of the `g//` command, so executed for each of those lines. Visually, it doesn't look like that, at a first sight. Explaining that part would improve this answer. – filbranden Sep 02 '19 at 16:29
  • ohh, Thank you for your instruction and advice – a4fz067lu Sep 03 '19 at 01:51
8

one-shot

You can declare a variable as the other answer does, or:

:%s/^x'\([^']*\)'/\=line('.')-1/  

Replace the last 1 by the line number of your header (id, phone, name) line if it doesn't sit in line 1.

two steps

You can get followings just by replacing 1 into 0 in your codes

id  phone   name
0   12345   jack
0   45678   Jojo
0    89456  Dio
0    34872  joseph

Then you can move your cursor to the first id 0, then press:

ctrl-v G g ctrl-a

It will turn the 0s into a sequence from 1

  • ctrl-v: column mode
  • G : select the 1st col till the last line.
  • g+ctrl-a: add
Kent
  • 189,393
  • 32
  • 233
  • 301
1

Alternatively you could use use awk, column, & Vim's filter command.

:%!gawk 'NR>1 {$1=++i} 1' | column -t

Explanation:

  • :%!{cmd} will "filter" the range, % (whole file), through command {cmd}
  • gawk '..' will run an awk one-liner
  • NR>1 {..} will run a "block" for record number greater than 1 (skips header)
  • $1=++i will set the first field to variable i (pre-incremented)
  • 1 is short of {print} which will print out the row
  • {cmd1} | {cmd2} will pipe the output from command, {cmd1}, to the input of command {cmd2}
  • column -t will pretty print tabular looking data
Peter Rincker
  • 43,539
  • 9
  • 74
  • 101
1

My PatternsOnText plugin has (among many others) a :Renumber command for this; it simplifies the typical solution with :substitute and a replacement expression:

:%Renumber/^x'\d\+'/
Ingo Karkat
  • 167,457
  • 16
  • 250
  • 324
0

Nice answers, one trick I use to enumerate large protobuf files:

First search/replace your target with a unique string like %%%%, then use the following: :let i=1 | g/%%%%/s//\=i/ | let i = i+1

e.g., to replace [ProtoMember()] [ProtoMember()] [ProtoMember()] with: [ProtoMember(1)] [ProtoMember(2)] [ProtoMember(3)]

First make all into [ProtoMember(%%%%)]

:% s/Member()/Member(%%%%)/g

then use

:let i=1 | g/%%%%/s//\=i/ | let i = i+1

GGleGrand
  • 1,565
  • 1
  • 20
  • 45