1

I have a situation like

something 'text' something 'moretext'

I forgot to add more spacing the first time I created this file and now on each line I should put some whitespace before the 2nd occurence of ' .

Now I can't build a regex for this.

I know that:

  • my command should begin with :%s because I want it to be executed on all lines
  • I should use the {2} operator to pick the 2nd occurence ?
  • If my regex will match something I can put stuff before the match with &

The main problem for me is how to build a regex to match the second ' using the {} notation, it's frustrating because I don't where it's supposed to be inserted or if I should use the magic or non-magic regex in vim.

The result I'm looking for

something 'text ' something 'moretext'
user2485710
  • 9,451
  • 13
  • 58
  • 102
  • it's really strange the fact that a regex like that is not that popular for `vim` users, I can't find nothing that is close to this, it shouldn't be easier to find the nth occurence of a char ? – user2485710 Mar 15 '14 at 08:02

3 Answers3

2

You can use

:%s:\v(^[^']*'[^']*)':\1 ':
  • [^'] means everything except '
  • \1 is a backreference to the first captured group (...)

Basically what your doing here is capturing everything up to the second quote, and replacing the line up to (and including) this quote with what you've captured, a space, and a quote.

Robin
  • 9,415
  • 3
  • 34
  • 45
  • so it's basically not an insert, it's a search and replace. I was hoping to learn about how to use that cryptic `{}` operator – user2485710 Mar 15 '14 at 07:54
  • you forgot `\v` it works for me but as `:%s/\v(^[^']*'[^']*)'/\1 '/` – user2485710 Mar 15 '14 at 07:55
  • Thanks, edited. Yep, it's a search & replace indeed. Rereading your question it does specify you want to use the `{}` notation, but I'm unfamiliar with it and can't help you with that. If you want answers to do it your specific way you should probably try to make that stand out more in your question. Have fun! – Robin Mar 15 '14 at 08:01
  • I'm really curious about that operator, doesn't seems to be that popular but from the docs it looks like it's a great tool for a regex ... that's what I find strange. – user2485710 Mar 15 '14 at 08:04
  • 1
    @user2485710: Does [this](http://stackoverflow.com/questions/5422949/how-to-find-the-3rd-occurrence-of-a-pattern-on-a-line#answer-5424784) relate? – Robin Mar 15 '14 at 08:09
  • why don't you use `:%s:\v(^[^']*'[^']*)':\1 ':`? Would be so much easier to read. I always get confused when there are too many `/` and `\\` – Francesco Montesano Mar 15 '14 at 08:11
  • @Robin nice, I should try that, to better understand your example 1 last question, what if I would like to revert the result which means "take everything and substract a trailing whitespace" ? – user2485710 Mar 15 '14 at 08:12
  • @FrancescoMontesano please elaborate more on a separate answer. – user2485710 Mar 15 '14 at 08:12
  • 1
    @user2485710: it's the same, it's just that I find it more readable. The `:s` command requires that the pattern and the substitution string are within 3 equal symbols (usually, as in the answer the `/`). but they can be anything. As usually you have some `\\` in the regex, I find much more readable to use something different from `/` to separate patter and substitution string. My choise usually falls to `:` (unless I need to do a substitution where `:` is required) – Francesco Montesano Mar 15 '14 at 08:17
  • @FrancescoMontesano: I wasn't sure you could choose your delimiters in vim, I agree this is more pleasant to read. Thx! @user: This becomes less clean (avoiding `.*?` when you can is usually a best practice for performance issues), but following the same logic you could use sth like `:\v(^[^']*'.*?) ':\1':` – Robin Mar 15 '14 at 08:19
  • @Robin I'm getting an error `(NFA regexp) Can't have a multi follow a multi` – user2485710 Mar 15 '14 at 08:34
  • removing `?` does the trick, I don't have an explanation for this but without that `?` it works as intended . – user2485710 Mar 15 '14 at 08:39
  • You may need to escape the `?` in vim `\v`mode, not sure... Without it on my previous pattern it would match until the last `'`. But actually `:\v(^[^']*'[^']*) ':\1':` will work in a more clean and efficient way, we can indeed avoid the `.` – Robin Mar 15 '14 at 08:44
2

{2} doesn't mean "the second match", it means "two matches" so it's completely useless for the task.

You could use a substitution like this one or the one in Robin's answer:

:%s/[^']*'[^']*\zs'/ '

Or you could use something like this:

:g/ '[^']*' /norm 2f'i<space>
romainl
  • 186,200
  • 21
  • 280
  • 313
  • ok, so I was wrong, but when you want "2 matches" in a regex ? sounds more like a conditional than a regex construct . – user2485710 Mar 15 '14 at 09:24
  • 1
    Quantifiers are extremely useful. How would you rather match a 10 digits number? `[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]`, `\d\d\d\d\d\d\d\d\d\d` or `\d\{10}`? – romainl Mar 15 '14 at 10:54
1

Yet another way to do it:

:%s/\v%([^']*\zs\ze'){2}/ /

Note: I am using very magic, \v, to reduce amount of escaping.

This approach uses \zs and \ze to set the start and end of the match . The \zs and \ze get set multiple times because of the quantifier, {2} but each occurrence of the group will change the \zs and \ze positions.

For more help see:

:h /\zs
:h /\v

Of course there is always sed, but the trick is getting the quote escaped correctly.

:%!sed -e 's/'\''/ &/2'
Peter Rincker
  • 43,539
  • 9
  • 74
  • 101