1

I have a .cpp file in which I would like to change all less-than symbols to double quotes but maintain any instances of double-greater-than symbols, i.e. change < to " while leaving all occurrences of << alone.

I am very new to vim and as such am still trying to figure out the best way to replace characters in situations like this.

4
  • So far I have tried the command :g/(<<)\@<!</s/</" but the result comes out like this: < << < becomes: " "< " Commented Apr 22, 2019 at 20:24
  • Maybe this? %s/\(^\|[^<]\)<\($\|[^<]\)/\1"\2/g Commented Apr 22, 2019 at 21:05
  • @JoãoA.Toledo That worked perfectly. Thanks! Commented Apr 22, 2019 at 21:11
  • 1
    @JoãoA.Toledo make that an answer! Id suggest the very-magic flag \v to cut down on the pattern noise though.
    – D. Ben Knoble
    Commented Apr 22, 2019 at 23:45

3 Answers 3

2

Just to make an answer out of my comment above: you could substitute all ocurences of < without a successor <:

%s/\(^\|[^<]\)<\($\|[^<]\)/\1"\2/g

Or, using the very-magic flag, as suggested by @d-ben-knoble:

%s/\v(^|[^<])\<($|[^<])/\1"\2/g

That means:

%s substitute in all lines

/\v starts search pattern and use very-magic flag, so we don't need to escape some characters

(^|[^<]) searches for a start of line (^) or one characters that is different from <

\< then searches for a < (the escape character here is needed)

($|[^<]) and finally searches for an end-of-line or a character that is different from <

/\1"\2/g and replaces all the findings with the first character, then " and the second character.

1

Use lookarounds

I think the simplest :substitute command might be to use lookarounds:

:%s/<\@<!<<\@!/"/g

This replaces every < character that is neither preceded nor followed by another < character.

How it works

It uses a :substitute command (See :help :substitute) to replace strings that match the regular expression <\@<!<<\@! with " characters.

The regular expression is reasonably simple when broken down. It consists of three parts:

:%s/<\@<!<<\@!/"/g
         ^
         1

In the middle is a single < character. This is what we want to match.

:%s/<\@<!<<\@!/"/g
    ^^^^^
      2

Before that is: <\@<!. This is a second < character followed by Vim's atom for negative lookbehind: \@<!, which means that our central < will only be matched if we didn't match a < here.

:%s/<\@<!<<\@!/"/g
          ^^^^
            3

At the end we have: <\@!. This is a third <, this time followed by Vim's atom for negative lookahead: \@!:our < will only be matched if it is not followed by a <.

See :help \@! and :help \@<! for more details of Vim's negative lookaround atoms.

1
  • Ah I see what's happening there. I tried something very similar to this before, but didn't use \@! when dealing with the third <. This seems like a clean way of solving this. Commented Apr 23, 2019 at 21:02
0

It's tricky to match single <, you have to consider behind and after character, which might also be start of line or end of line. An alternative is to match both of them, use \= to deal with <<

%s/\V<<\|</\=submatch(0)=='<'?'"':'<<'/g
  • \V<<\|< match << or < if no << exists
  • \=submatch(0)=='<'?'"':'<<' replace < with ", << with <<
    • \= substitute with an expression, check :h sub-replace-expression
    • submatch(0) the whole matched text in current :substitute command.
    • submatch(0)=='<'?'"':'<<' return " for <, << for <<
2
  • 1
    I don't think you need the very-nomagic switch \V: your expression works without it. Also, I'd argue that <<\? is simpler than `<<\|<` but I guess that's a matter of preference.
    – Rich
    Commented Apr 23, 2019 at 13:59
  • Thanks, <<\? is simpler, it's better then |. I add \v or \V to all my regex, their rule is simple.
    – dedowsdi
    Commented Apr 23, 2019 at 14:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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