8

I guess I'm not very good at regular expressions, so here is my problem (I'm sure it's easy to solve, but somehow I can't seem to find how )

var word = "aaa";
text = text.replace( new RegExp(word, "g"),
                     "<span style='background-color: yellow'>"+word+"</span>");

this is working in most cases.

What I want to do is for the regex ONLY TO WORK when word is not followed by the char / and not preceded with char ".

MirrorMirror
  • 186
  • 8
  • 36
  • 70

3 Answers3

15

You're going to want to use a negative look-ahead.

Regex: '[^"]' + word + '(?!/)'

Edit: While it doesn't matter as it appears you already found your answer by avoiding look-behinds, Rohit noticed something I didn't. You're going to need to capture the [^\"] and include it in the replace so that it does not get discarded. This wasn't necessary for the look-head since look-arounds by definition aren't included in captures.

Dan
  • 919
  • 1
  • 10
  • 28
Michael
  • 3,334
  • 20
  • 27
  • thanks alot. so if i also want it to not to be preceded by char " i should make it "(?!\")" + word + "(?!\/)" ? – MirrorMirror Dec 24 '12 at 08:46
  • If you want to make sure " does not precede word, you need to use a negative look-behind. So you would need `(?<!\") + word + (?!\/)`. Check out this link, it's where I learned how to use these: http://www.regular-expressions.info/lookaround.html – Michael Dec 24 '12 at 08:48
  • 15
    Javascript does not supprt look-behinds. – Rohit Jain Dec 24 '12 at 08:51
  • new RegExp("(?<!\")"+word+"(?!\/)", "g") says invalid quantifier when i add the look behind. – MirrorMirror Dec 24 '12 at 08:52
  • @RohitJain Ah well then since negative look-behinds don't work, you can just use a negated character class. `[^\"] + word + (?!\/)`. Thanks for the heads up. – Michael Dec 24 '12 at 08:54
  • indeed it can't look behind. i solved it by checking two characters, as it should not find it if it also has a number after the slash, so no need to look behind. solution: word+"(?!\/[0-9])", "gi". the preceding [^\"] that you posted breaks things. – MirrorMirror Dec 24 '12 at 09:01
  • 1
    `'[^"]' + word + '(?!/)'` is enough. – nhahtdh Dec 24 '12 at 09:03
4

You can use this regex: -

'([^"])' + word + '(?!/)'

and replace it with - "$1g"

Note that Javascript does not support look-behinds. So, you need to capture the previous character and ensure that it is not ", using negated character class.

See demo at http://fiddle.re/zdjt

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • This fails if the match is at beginning or end of line. Perhaps the question could mention whether this matters. – tripleee Dec 24 '12 at 09:02
0

First of all, the question can be interpreted as

  1. match word not preceded AND not followed ("aaa and aaa/ should match, same as "aaa/) and as
  2. match word not preceded OR not followed with some char(s) ("aaa, aaa/ should not match, while "aaa/ should).

If you are using a JavaScript environment that supports ECMAScript 2018, you can use a lookbehind solution:

const word = 'aaa',                     // Word to find
    left = '"',                         // Left-hand negative marker
    right = '/',                        // Right-hand negative marker
    text = 'aaa aaa/ "aaa "aaa/ aaaaa'; // Test string

// Relace any 'word' that does not start with " OR does not end with /
var regex = new RegExp(String.raw`\b(?<!${left})${word}\b(?!${right})`, 'g')
console.log( text.replace(regex, "<span style='background-color: yellow'>$&</span>") );
// => <span style='background-color: yellow'>aaa</span> aaa/ "aaa "aaa/ aaaaa

// Replace any 'word' that does not start with " AND does not end with /
var regex = new RegExp(String.raw`\b${word}\b(?!(?<=${left}${word})${right})`, 'g')
console.log( text.replace(regex, "<span style='background-color: yellow'>$&</span>") );
// => <span style='background-color: yellow'>aaa</span> <span style='background-color: yellow'>aaa</span>/ "<span style='background-color: yellow'>aaa</span> "aaa/ aaaaa

If you are using a JavaScript environment that does not support ECMAScript 2018, you can use

var word = 'aaa',                       // Word to find
    left = '"',                         // Left-hand negative marker
    right = '/',                        // Right-hand negative marker
    text = 'aaa aaa/ "aaa "aaa/ aaaaa'; // Test string

// Relace any 'word' that does not start with " OR does not end with /, one match expected
var regex = new RegExp("([^" + left + "\\w]|^)(" + word + ")\\b(?!" + right + ")", 'g')
console.log( text.replace(regex, "$1<span style='background-color: yellow'>$2</span>") );
// => <span style='background-color: yellow'>aaa</span> aaa/ "aaa "aaa/ aaaaa

// Same as above, but in case "left" is a sequence of chars, not a single char, or a longer pattern:
var regex = new RegExp("(" + left + ")?\\b(" + word + ")\\b(?!" + right + ")", 'g')
console.log( text.replace(regex, function(x,g,gg) { return g ? x : "<span style='background-color: yellow'>" + gg + "</span>";}) );
// => <span style='background-color: yellow'>aaa</span> aaa/ "aaa "aaa/ aaaaa

// Replace any 'word' that does not start with " AND does not end with /, three matches expected
var regex = new RegExp("(" + left + word + right + ")|\\b" + word + "\\b", 'g')
console.log( text.replace(regex, function(x, g) { return g || "<span style='background-color: yellow'>" + x + "</span>";}) );
// => <span style='background-color: yellow'>aaa</span> <span style='background-color: yellow'>aaa</span>/ "<span style='background-color: yellow'>aaa</span> "aaa/ aaaaa

Note:

  • I use whole word matching here via \b, word boundary (and \w construct in the negated character class in one of the workarounds)
  • If right and left chars/strings contain special regex metacharacters, mind to escape them before using as part of a regex
  • Same escaping need might arise for the word variable, but in that case, if the word starts or/and ends with a special regex metacharacter, word boundaries (\b) will no longer work, you will need to use adaptive/dynamic word boundaries.
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563