Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace character if not preceded nor followed by same character

I am trying to replace non-consecutive single quotes in a string with two consecutive quotes.

Examples (in/out)

  • "abc'def" --> "abc''def"
  • "abc''de'f" --> "abc''de''f"
  • etc.

Javascript doesn't support lookbehinds, so the following regular expression I'd use with Java (well, more or less) will not compile:

myString.replace(/(?<!)'(?!'))/g, "''");

I have looked around SO and some answers advise using a non-capturing group containing a custom character class negating the character that would otherwise be in the negative lookbehind:

myString.replace(/(?:[^'])'(?!'))/g, "''");

However, that will not do either: it will successfully not replace the two consecutive single quotes, but in the "abc''de'f" example, it will "eat" the f when replacing the next single quote with two consecutive single quotes, ending up in:

"abc''de''gh" (see it's missing the f!)

Questions

  • Is there a suitable regex-based solution for this problem?
  • If not, should I just go with a barbaric iteration and indexing of all the input string's characters and painfully build another string from it (please no)?
like image 476
Mena Avatar asked Oct 18 '25 06:10

Mena


2 Answers

You can use this regex:

str = str.replace(/(^|[^'])'(?!')/g, "$1''"));
  • It matches line start or non single quote character before a single quote and captures it in a group #1.
  • Using a negative lookahead it also asserts that matched single quote is not followed by another single quote.
  • In replacement we use back-reference of captured group #1 and two ''

Full code:

var arr = ["abc'def", "abc''de'f"];

for (i=0; i<arr.length; i++) {
   console.log( arr[i] + ' => ' + arr[i].replace(/(^|[^'])'(?!')/g, "$1''") );
}

Output:

abc'def => ab''def
abc''de'f => abc''d''f
like image 86
anubhava Avatar answered Oct 20 '25 18:10

anubhava


You may also match 2 or more occurrences of single apostrophes into a capturing group and just match all other single apostrophes, and use a callback to use the right replacement in either cases:

var ss = ["abc'def" , "abc''de'f", "abc''''''def'g"];
for (var s of ss) {
  console.log(s.replace(/('{2,})|'/g, function($0,$1) { return $1 ? $1 : "''"; }));
}

This solution won't shrink ''''' to '' as replace(/'+/g, "''") would.

Details:

  • ('{2,}) - matches and captures into Group 1 two or more occurrences of '
  • | - or
  • ' - a single apostrophe is matched in all other contexts.

The callback method accepts $0 (the whole match) and $1 (Group 1). If $1 is not undefined, then its value is reinserted, else ' (the whole match) is replaced with ''.

like image 27
Wiktor Stribiżew Avatar answered Oct 20 '25 18:10

Wiktor Stribiżew



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!