43

Regex test() is giving me issues in Firefox and Chrome, yet it works flawlessly in Opera and Safari.

troubled code:

var pattern = /(\s+(?!\$\w+)|(^(?!\$\w+)))/g;
if(pattern.test(String(id).replace(/\s+OR|AND\s+/g, ''))) {
 searchError("You suck.");
 return 1;
}

When you pass in white space, it blocks it every time. When you pass in something like '$a b' then it will work every other time in Firefox/Chrome. WEIRD.

Jacksonkr
  • 31,583
  • 39
  • 180
  • 284

2 Answers2

95

It's a bug in the RegEx engine, a similar question with the same issue came up here.

From my answer to that question: It's a bug with the way regexes are implemented in ECMAScript 3, there's a great post on the details here.

The basics are a /regex/ with the g modifier doesn't reset correctly, so multiple .test() calls alternate between true and false if everyone should be true, every other calls successfully resets it.

Community
  • 1
  • 1
Nick Craver
  • 623,446
  • 136
  • 1,297
  • 1,155
  • Oh snap. you guys are awesome. Thanks for the quick replies and helpful links (this was a tough one to search solutions for). – Jacksonkr Oct 08 '10 at 14:47
  • 1
    @Jackson, if this answers your question (it should), then click the little check-mark, to the left -- to mark it as the accepted answer. – Brock Adams Oct 09 '10 at 00:23
  • @Brock Adams This must have skipped my attention. Thanks for the reminder. – Jacksonkr Oct 22 '10 at 17:20
  • 1
    Which absolutely sucks if you're checking something with a statement like this, looking for a match in either of two item fields `if (regex.test( item.name) || regex.test( item.description ))` where half the expressions match half the time, depending on the search term order, resulting in extremely illogical search results. Shocked this stil hasn't been fixed (May 2014). – Triynko May 13 '14 at 23:25
  • 1
    The problem is not that it doesn't reset correctly (as [that is exepected](http://stackoverflow.com/q/1520800/1048572)), but that the literal is "static" and yields the same RegExp object every time it is evaluated - not having a fresh `.lastIndex` property either. Of course, that's only ES3. – Bergi May 15 '15 at 03:02
  • 1
    Just ran into this on Chrome 42.. news to me. – SgtPooki May 20 '15 at 22:31
  • 4
    what the flipping heck... i was going insane why my `console.log` was returning `true` yet my `if` condition straight after was `false` – andy mccullough May 31 '18 at 15:06
  • 5
    I'm just here to say that this issue is still occurring 10 years later in Chrome 81 – Reverate Apr 27 '20 at 18:52
  • 1
    And in node v13.11.0, as of May 4th 2020. I was going absolutely insane. – Johnny Bueti May 04 '20 at 07:44
  • 2
    For me wrapping the regex, e.g. `pattern` to ` (new RegExp(pattern))` seems to work – prototype May 15 '20 at 15:07
  • I don't know the history of this issue, but it is described under **Using test() on a regex with the "global" flag** section in [MDN test method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test) - so, I wouldn't call it a bug, but a rather weird feature... – Sundeep Jul 29 '20 at 14:17
  • 1
    This is a feature to test for multiple matches. If you match a new string, it is good practice to first set `r.lastIndex = 0` before using `test`. You may also use it in one line if-statement like so: `if(r.lastIndex = 0 || r.test(str)){ ... }`. – Yeti Jun 17 '21 at 09:22
5

This seems to still be an issue in August of 2021... I just want to share some things I have learned before stumbling upon this question and answer. I was baffled by this problem and had no meaningful way forward - until now.

  1. It doesn't matter whether you use exec() or test() or match(). The regex still doesn't work properly on every other occurrence.

  2. It doesn't matter if you set the regex with

let reg = new RegExp(/<table(\s*[^>]*)>/g);

or with const. Doesn't matter if you set it globally or locally either...

What does work to bypass this problem is wrapping your regex statement in parenthesis in your loop like so:

Object.keys(table).forEach(key => {
    if((new RegExp(/<table(\s*[^>]*)>/g)).test(___your test string___)){
        //Do what you need to do
    }
});

Remove the parenthesis around the Regex, and watch every other one fail....

Thank you so much for the answer @Nick Craver and the comment @prototype!

This exact Regex was what was giving me trouble. It would work for one object, and fail for the subsequent object, and it made no sense. I am only here to say that this is still very relevant in 2021.

halfer
  • 19,824
  • 17
  • 99
  • 186
Willie
  • 281
  • 3
  • 21
  • 1
    Look @halfer I appreciate the edit, but I don't appreciate the removal of "Thank God". Seriously, I couldn't find an answer anywhere for days, and finally found this answer already posted. If things like this aren't a gift from God at times, then I don't know what is... I'd rather just delete this "answer" (which really only illustrates the spread of the regex problem to other areas) if you're going to strip God out of it. – Willie Aug 11 '21 at 17:31
  • I understand your view, and sympathise. We have had a few people here, over the years, wanting to add theistic, religious, superstitious, and other such material to their posts, and the community view is that it does not represent the technical, neutral view we are aiming for. We have a [canonical discussion here](https://meta.stackoverflow.com/questions/295810/do-we-allow-religious-invocations-in-questions-answers). – halfer Aug 11 '21 at 21:36
  • The thing with Stack Overflow is that we're sort of creating documentation for many future readers, and this can come as a surprise to new members. I acknowledge that it takes a bit of getting used to, given that for some decades the "forum model" encouraged contributors to think of posts as their own material (over which they could exercise full control). We are perhaps more like a mutually editable wiki here. – halfer Aug 11 '21 at 21:39