'Regular Expression - Match String Not Preceded by Another String (JavaScript)

I am trying to find a regular expression that will match a string when it's NOT preceded by another specific string (in my case, when it is NOT preceded by "http://"). This is in JavaScript, and I'm running on Chrome (not that it should matter).

The sample code is:

var str = 'http://www.stackoverflow.com www.stackoverflow.com';
alert(str.replace(new RegExp('SOMETHING','g'),'rocks'));

And I want to replace SOMETHING with a regular expression that means "match www.stackoverflow.com unless it's preceded by http://". The alert should then say "http://www.stackoverflow.com rocks", naturally.

Can anyone help? It feels like I tried everything found in previous answers, but nothing works. Thanks!



Solution 1:[1]

As JavaScript regex engines don't support 'lookbehind' assertions, it's not possible to do with plain regex. Still, there's a workaround, involving replace callback function:

var str = "As http://JavaScript regex engines don't support `lookbehind`, it's not possible to do with plain regex. Still, there's a workaround";

var adjusted = str.replace(/\S+/g, function(match) {
  return match.slice(0, 7) === 'http://'
    ? match
    : 'rocks'
});
console.log(adjusted);

You can actually create a generator for these functions:

var replaceIfNotPrecededBy = function(notPrecededBy, replacement) {
   return function(match) {
     return match.slice(0, notPrecededBy.length) === notPrecededBy
       ? match
       : replacement;
   }
};

... then use it in that replace instead:

var adjusted = str.replace(/\S+/g, replaceIfNotPrecededBy('http://', 'rocks'));

JS Fiddle.

Solution 2:[2]

raina77ow's answer reflected the situation in 2013, but it is now outdated, as the proposal for lookbehind assertions got accepted into the ECMAScript spec in 2018.

See docs for it on MDN:

Characters Meaning
(?<!y)x Negative lookbehind assertion: Matches "x" only if "x" is not preceded by "y". For example, /(?<!-)\d+/ matches a number only if it is not preceded by a minus sign. /(?<!-)\d+/.exec('3') matches "3". /(?<!-)\d+/.exec('-3') match is not found because the number is preceded by the minus sign.

Therefore, you can now express "match www.stackoverflow.com unless it's preceded by http://" as /(?<!http:\/\/)www.stackoverflow.com/:

const str = 'http://www.stackoverflow.com www.stackoverflow.com';
console.log(str.replace(/(?<!http:\/\/)www.stackoverflow.com/g, 'rocks'));

Solution 3:[3]

This also works:

var variable = 'http://www.example.com www.example.com';
alert(variable.replace(new RegExp('([^(http:\/\/)|(https:\/\/)])(www.example.com)','g'),'$1rocks'));

The alert says "http://www.example.com rocks".

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 CherryDT
Solution 3 Assaf Hershko