2

I need to find and replace multiple occurrences of a character after another character.

My file looks like this:

b
a
b
b

And I need to replace all b after a with c:

b
a
c
c

I came up with this: a((\n|.)*)b as the find expression and a$1c as the replace option, however it only replaces the last match instead of all of them.

I am using VSCode's global search and replace option.

I found a dirty way to achieve what I want: I add a ? lazy quantifier after .* matches once, and I apply the replacement. Then I can do it again and it will replace the next match. I do this until all occurrences are replaced.

However this would not be usable if there are thousands of matchs, and it would be very interesting to know if there is a proper way to do it, with only 1 find.

How can I match all b after a?

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
papillon
  • 1,807
  • 2
  • 19
  • 39

2 Answers2

3

It's probably not the prettiest, but when tried and tested the following worked for me:

(?:^a\n|\G(?<!\A))\n*\Kb$

See the online demo. I don't know VSCode but a quick search let me to believe it should follow Perl based PCRE2 syntax as per the linked demo.

  • (?: - Open non-capture group:
    • ^a\n - Start line anchor followed by "a" and a newline character.
    • | - Or:
    • \G(?<!\A) - Meta escape, assert position at end of previous match or start of string. The negative lookbehind prevents the start of string position to be matched.
    • ) - Close non-capture group.
  • \n* - 0+ new-line characters.
  • \K - Meta escape, reset starting point of reported match.
  • b$ - Match a literal "b", followed by an end-line anchor.
JvdV
  • 70,606
  • 8
  • 39
  • 70
3

You can use

(?<=a[\w\W]*?)b

Replace with c. Details:

  • (?<=a[\w\W]*?) - a positive lookbehind that matches a location that is immediately preceded with a and then any zero or more chars (as few as possible)
  • b - a b.

Also, see Multi-line regular expressions in Visual Studio Code for more ways to match any char across lines.

Demo:

enter image description here

After replacing:

enter image description here

If you need to use something like this to replace in multiple files, you need to know that the Rust regex used in the file search and replace VSCode feature is really much less powerful and does not support neither \K, nor \G, nor infinite-width lookbehinds. I suggest using Notepad++ Replace in Files feature:

enter image description here

The (?:\G(?!\A(?<!(?s:.)))|a)[^b]*\Kb pattern matches

  • (?:\G(?!\A(?<!(?s:.)))|a) - either of the two options:
    • \G(?!\A(?<!(?s:.))) - the end of the previous successful match ((?!\A(?<!(?s:.))) is necessary to exclude the start of file position from \G)
    • | - or
    • a - an a
  • [^b]* - any zero or more occurrences of chars other than b
  • \K - omit the matched text
  • b - a b char.
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • For reasons I do not know, it doesn't work as a global search : "lookbehind assertion is not fixed length" :/ probably some kind of restriction for performance... – papillon Apr 12 '21 at 13:56
  • @papillon You can only use this regex in the Document Find&Replace feature, not in the File Search and Replace. – Wiktor Stribiżew Apr 12 '21 at 14:03
  • 1
    @papillon You can actually use Notepad++ with the expression like JvdV's one, to replace inside multiple files. My regex will look like `(?:\G(?!\A(?<!(?s:.)))|a)[^b]*\Kb` in Notepad++. – Wiktor Stribiżew Apr 12 '21 at 14:09