0

I have a file

0,3,
7,2,0,
1,2,3,
1,2,

which should be

0,3,0,
7,2,0,
1,2,3,
1,2,0,

So how can I append each line with two commas with 0,? I am interested in the most elegant solution and particularly interested if some script exists to help with such tasks, often required in manipulating CSV files.

1

The most elegant way I could think of would be with a substitute command.

Either of these would do the trick:

:%s/\v^(\d,){2}$/\00,
:%s/^\d,\d,$/\00,

This will replace any line containing two digits and commas with that whole line followed by "0,". If you would like to modify it to work with numbers more than 1-digit long, you could change it to:

:%s/\v^(\d+,){2}$/\00,

or

:%s/^\d\+,\d\+,$/\00,
  • What does \v mean? \v to stand for very magic making the characters to have special meaning so you don't have to escape the brackets? While \V turns off the very magic property, right? Found it here: vim.wikia.com/wiki/… Nice command +1 – hhh Sep 27 '18 at 11:31
1

I don't know if this qualifies as elegant or overkill but anyways... :)

As an idealistic software dude whenever I see a problem like this I tend to think in terms of a general solution. Here's a script I just wrote that will determine the arbitrarily maximum number of columns in a CSV file and then pad out all the lines with fewer columns so they are the same size.

" Recursively look for line with most columns and return that number
func! MaxCsvCols(maxcols)
    " find next line with equal to or greater than maxcols columns
    let line = search('^\([^,]\+,\)\{' . a:maxcols . ',}$', 'W')
    if line != 0
        return MaxCsvCols(len(split(getline("."), ",")))
    else
        return a:maxcols
    endif
endfunc

func! PadCsv()
    call cursor(1, 1)
    let maxcols = MaxCsvCols(1)
    if maxcols < 2 | return | endif

    call cursor(1, 1)
    let linenum = 1
    while linenum > 0
        let newcols = maxcols - len(split(getline("."), ","))
        let newtext = getline(".") . repeat("0,", newcols)
        call setline(linenum, newtext)
        " find next line that has less-than maxcols columns
        let linenum = search('^\([^,]\+,\)\{,' . eval(maxcols-1) . '}$', 'W')
    endwhile
endfunc

With the cursor somewhere in this...

0,3,
7,2,0,
1,2,3,
7,2,0,9,
6,
1,2,3,2,2,
1,2,

...do :call PadCsv() and you get this...

0,3,0,0,0,
7,2,0,0,0,
1,2,3,0,0,
7,2,0,9,0,
6,0,0,0,0,
1,2,3,2,2,
1,2,0,0,0,

Of course this will work with your example, too.

If there's a much easier way to do this in Vim I'd love to see it...but I probably would have written this anyways. I like to code. ;)

0

A solution

:g!/[0-9],[0-9],[0-9],/ s/$/0,/

where it matches all lines with two commas and three numbers. Then the bang ! reverses it to get what wanted.

  • 3
    Two tips: 1) Surely you want [0-9], rather than [0-9,]. The second one includes the comma in the range, meaning it will match a digit or a comma. 2) Your regex could be way simplified: :g!/\(\d,\)\{3} or even :g!/\v(\d,){3} – DJMcMayhem Sep 26 '18 at 20:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.