Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I've got a large CSV file which is supposed to have two fields. It should look basically like this:

1234, Some series of words
539345, Something else
2039, "quotes are, of course, necessary here"

The problem is that the quotes on line 3 are missing, and there are many lines like this. I have recorded a macro to do the following:

  • Go to the beginning of a line
  • Move to the spot just after the first comma
  • Insert a quotation mark
  • Go to the end of the line
  • Append a quotation mark

However, even with this macro, I have to manually scan through the file, playing it back on lines with multiple commas. I can't do it on every line, because that's not a valid CSV.

What I'd like is a substitution command that says: "for lines with more than one comma, place a quote after the first comma and a quote at the end of the line."

Anybody care to take a swing at that?

share|improve this question
    
Actually, I think that it is OK to do this on every line, in which case I can just run my macro on the whole file. But I'm still interested in seeing an answer. –  Nathan Long Jan 21 '11 at 22:13
    
what is 'vimgolf'? –  akira Jan 22 '11 at 5:38
1  
vimgolf is solving a problem in the fewest number of keystrokes within vim. –  OmnipotentEntity Jan 22 '11 at 7:51

2 Answers 2

up vote 3 down vote accepted

Here is a shorter command without any capture groups. It uses & to repeat the previous match. There are two commands separated by |. The first adds the quote after the first "," and the spaces that follow it, if any. The second adds a quote at the end of the line.

:g/,.*,/s/, */&"/|s/$/"/
share|improve this answer
    
'The first adds the quote after the first ","' .. if there is no " already (which is what OP wants, but should be clarified). and prepending blindly a " to $ creates 'necessary here""' given OPs 3. sample, doesnt it? –  akira Jan 22 '11 at 5:37
    
@akira: I took the samples to be the desired "after" rather than the "before". My command has the same affect as garyjohn's, but is shorter. If you wanted to make sure that the last character is not already a double quote, change the second command to s/[^"]$/&"/ –  Dennis Williamson Jan 22 '11 at 8:09
    
Great! Two things: I didn't know you could pass the results of :g to a substitution - do you know where I can read more on this? 2) Does the | separator here work like it does in bash - pipe the output of the first command to the input of the second? –  Nathan Long Jan 25 '11 at 13:59
    
@GorillaSandwich: The | is a command separator like ; in Bash. It allows you to execute more than one command. It doesn't pipe the output of one into the other. See :h :bar. You can do :h global to see more information about g (which accepts a command as an argument) and :h / to see more about searching (which is the command in this case). –  Dennis Williamson Jan 25 '11 at 15:41
1  
@Dennis Williamson: Not that I know of, but you can prefix an ex command with :silent! to ignore errors. In :silent! <command 1> | <command 2>, <command 2> will be executed even if <command 1> generates an error. –  garyjohn Jan 25 '11 at 16:32

Sure.

:g/,.*,/s/\(, *\)\(.*\)/\1"\2"/

The g/,.*,/ searches for any line containing at least two commas. The substitute command searches for a comma followed by zero or more spaces and puts that into match buffer #1, then puts everything else on the line into match buffer #2. Finally, the substitute command replaces those two matches by the first match, then the second match enclosed in quotes.

share|improve this answer
    
Thanks for your help! I accepted Dennis Williamson's answer because it's shorter, but both of you gave good working solutions. I'll ask you the same question I asked him: I didn't know you could pass the results of :g to a substitution - do you know where I can read more on this? –  Nathan Long Jan 25 '11 at 14:00
    
@GorillaSandwich: Dennis Williamson is not passing the result of :g to a substitution; he is passing the match of the s command's pattern to the substitution. That is, & represents the match to ", *", not the match to ",.*,". That said, you can pass the result of :g to a substitution by leaving the s command's pattern empty, e.g., :g/.*/s//"&"/. I don't have a very good reference for this, but you can find out more from :help s/\& and :help mulit-repeat (skip to the second paragraph from the bottom of the section). –  garyjohn Jan 25 '11 at 15:37
    
@garyjohn: FYI, you could do less escaping if you used \v: :g/,.*,/s/\v(, *)(.*)/\1"\2"/. –  idbrise Dec 5 '12 at 18:15

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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