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.

  • So far I have tried the command :g/(<<)\@<!</s/</" but the result comes out like this: < << < becomes: " "< " – Justin Waterhouse Apr 22 at 20:24
  • Maybe this? %s/\(^\|[^<]\)<\($\|[^<]\)/\1"\2/g – João A. Toledo Apr 22 at 21:05
  • @JoãoA.Toledo That worked perfectly. Thanks! – Justin Waterhouse Apr 22 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 Apr 22 at 23:45
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.

  • 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. – Justin Waterhouse Apr 23 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 <<
  • 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 Apr 23 at 13:59
  • Thanks, <<\? is simpler, it's better then |. I add \v or \V to all my regex, their rule is simple. – dedowsdi Apr 23 at 14:14

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.