0

I suck at regex. I have the following expression:

(?!.*\")(Level)(\s)([0-9])(?!.*\")

I want to use this expression to replace all Level 2 to "Level 2" with queries. The problem is that this regex catches "Level 2(without closing "). I want to catch Level 2 only if it has to quotes before AND after the words.

What is the best approach to do that?

Filip
  • 346
  • 1
  • 4
  • 15

3 Answers3

0

If you just want to capture every occurrence of Level\s+\d+ and replace it with "Level\s+\d+" provided it is not enclosed by doublequotes from both sides, you may use try using this regex,

([^"]|^)(Level\s+\d+)([^"]|$)

and replace it with,

\1"\2"\3

Check here for a demo

This will not choose a text for replacement of type Label 2" or "Label 2 or "Label 2" and only replace when Label 2 when it is not surrounded by double quote from either side.

Pushpesh Kumar Rajwanshi
  • 18,127
  • 2
  • 19
  • 36
  • 1
    Not working ok, because it also replaces `"Level 2"` with `""Level 2""` https://regex101.com/r/kuTf1k/1 – Filip Nov 20 '18 at 17:57
  • @Filip: Ok I've updated the answer to ensure it is not already surrounded by double quotes. Can you check now? – Pushpesh Kumar Rajwanshi Nov 20 '18 at 18:02
  • @PushpeshKumarRajwanshi You suggest something that only works with ECMAScript 2018 but even [ES6 syntax does not work for OP](https://stackoverflow.com/questions/53398711/javascript-regex-negativ-lookahead-match#comment93672805_53398711). – Wiktor Stribiżew Nov 20 '18 at 18:04
  • oh ok Wiktor, sorry I forgot it was javascript where look behind doesn't work :( Let me figure out something else. – Pushpesh Kumar Rajwanshi Nov 20 '18 at 18:05
  • Yeah, I need something that works natively in browser as this is in a simple file not transpilled by babel. – Filip Nov 20 '18 at 18:08
  • Ok, I've updated with a regex which doesn't use any look arounds. Let me know if this works fine for you. I've updated the regex101 url to add more cases to show it works in different cases where if Level 2 is at the very start of string or at the very end. – Pushpesh Kumar Rajwanshi Nov 20 '18 at 18:14
  • @PushpeshKumarRajwanshi It won't work in case of `Level 2`s that are consecutive, i.e. that occur after a single char one after another. See [my solution](https://stackoverflow.com/a/53399080/3832970) that does what you mean to write and is generic (matches any digits after `Level`). – Wiktor Stribiżew Nov 20 '18 at 18:15
  • @WiktorStribiżew: Your solution is good but OP really hasn't mentioned anything about whether how to treat this kind of text ("another Level 5") as you have accounted for in your example. With lack of enough samples provided by OP, I would call this post as too broad. May be OP wants to change this string in input "another Level 5" to "another "Level 5"" or may be to this "another "Level 5" which is not clear. I can of course generate a solution for all logically valid cases for OP but due to lack of it, my solution will remain incomplete. Hope OP agrees with your answer and accepts it. – Pushpesh Kumar Rajwanshi Nov 20 '18 at 18:22
0

You may use

var s = '"Level 3 protected": Level 4 here and "another Level 5"';
s = s.replace(/(^|[^"])(Level\s+\d+)(?!["\d])/g, '$1"$2"');
console.log(s);

Details

  • (^|[^"]) - Group 1 ($1): start of string or any char but a double quote
  • (Level\s+\d+) - Group 2 ($2): Level, 1+ whitespaces, 1+ digits
  • (?!["\d]) - no digit or double quote to the left of the current location is allowed.

See the regex demo.

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
0

Not sure if you want to replace "Level 2 and Level 2" or not, so I offer solutions for both scenarios:

1) If you want to replace only those with not even a single ":

(?<!")(Level\s\d+)(?!")

see https://regex101.com/r/Nc7b5h/1

This uses a negative look-behind and a negative look-ahead


2) However, if you also want to replace those with only one ", try this:

(?!"Level\s\d+")(?<!")"?Level\s\d+"?

https://regex101.com/r/qL5Li5/1/

Set your match group based on whether you want to replace the single " or not:

var s='This "Level 2" is "Level 2 a Level 2" test Level 2 :)'
​
s.replace(/(?!"Level\s\d+")(?<!")"?(Level\s\d+)"?/g, '"$1"');

//> 'This "Level 2" is "Level 2" a "Level 2" test "Level 2" :)'

or

var s='This "Level 2" is "Level 2 a Level 2" test Level 2 :)'

s.replace(/(?!"Level\s\d+")(?<!")("?Level\s\d+"?)/g, '"$1"');

//> 'This "Level 2" is ""Level 2" a "Level 2"" test "Level 2" :)'

The trick here was to first perform a negative look-ahead for the forbidden case of a match with two ", before doing anything else.

General Notes

If you don't want to use a look-behind expression ((?<!")) as older browsers (pre-ES2018) won't support that, just use (^|[^"]) instead, as other suggested. Just don't forget to restore its contents in the replacement :)

e.g. s.replace(/(?!"Level\s\d+")(^|[^"])"?(Level\s\d+)"?/g, '$1"$2"');

Jay
  • 3,640
  • 12
  • 17