46

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.

2
  • Replace id with something static and see if that works.
    – Alin P.
    Commented Oct 8, 2010 at 14:44
  • 1
    omg :-) seems like a fun bug ...
    – Run CMD
    Commented Oct 8, 2010 at 14:46

3 Answers 3

97

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.

12
  • 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. Commented Oct 9, 2010 at 0:23
  • 4
    what the flipping heck... i was going insane why my console.log was returning true yet my if condition straight after was false Commented May 31, 2018 at 15:06
  • 6
    I'm just here to say that this issue is still occurring 10 years later in Chrome 81
    – Reverate
    Commented Apr 27, 2020 at 18:52
  • 2
    For me wrapping the regex, e.g. pattern to ` (new RegExp(pattern))` seems to work
    – prototype
    Commented May 15, 2020 at 15:07
  • 2
    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
    Commented Jun 17, 2021 at 9:22
6

Update: 9/27/23 - I am STILL running into this issue.

Consider the following:

  1. You are using Angular reactive forms, and are using some built in features, like out of the box Validators.
  2. You use something like the following.
      form.forEach((formField: any) => {
        let validatorsArray = formField.validators?.reduce((validators: any, validator: any) => {
          switch(validator.type){
            case 'required':
              validators.push(Validators.required);
              break;
            case 'pattern':
              validators.push(Validators.pattern(new RegExp(validator.value)));
              break;

where each validator is an object I've defined on our server like so (for example):

{
"name": "lowercaseandunderscore"
"value": "^[a-z_]*$",
"type": "pattern",
"error": "Column names must only contain lower case letters and underscores"
}

Now, one would assume this would inherently work using reactive forms, but it does not! It falls into the same pit as the post in 2021 below. Don't be fooled!

I came across this today.

Oddly, what works is the following:

      form.forEach((formField: any) => {
        let validatorsArray = formField.validators?.reduce((validators: any, validator: any) => {
          switch(validator.type){
            case 'required':
              validators.push(Validators.required);
              break;
            case 'pattern':
              validators.push(Validators.pattern(new RegExp(/^[a-z_]*$/)));
              break;

This does not fail every other time!

But, if you are creating a Regex from using the class, using a STRING... watch out. You'll have to do something like this.

      form.forEach((formField: any) => {
        let validatorsArray = formField.validators?.reduce((validators: any, validator: any) => {
          switch(validator.type){
            case 'required':
              validators.push(Validators.required);
              break;
            case 'pattern':
              validators.push(this.testValue(validator));
              break;

where your testValue function is the following:

private testValue(validator: any): ValidatorFn {
    return (control: AbstractControl) => {
      let returnVal = null;
      if(control.value){
        //NOTE the use of parenthesis around the RegExp below.
        if((new RegExp(validator.value, 'gm').test(control.value))){
          return null;
        } else {
          return {[validator.name]: validator.error};
        }
      }
      return returnVal;
    }
  }

As I was working through this today, I honestly thought "surely I can't be facing that same exact problem with regexes...". Turns out, I was.

There is something to be said for every time you create a RegExp with a string. Just wrap it in parenthesis. For whatever reason, creating it without a string literal works like it should.

I hope this hammers that point home.

Essentially, regexes seem to misbehave when created from string literals, and seem to be just fine when created NOT from a string.

///End of update

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.

3
  • 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
    Commented Aug 11, 2021 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.
    – halfer
    Commented Aug 11, 2021 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
    Commented Aug 11, 2021 at 21:39
0

for some reason, matchAll() works correctly even though test() doesnt

Not the answer you're looking for? Browse other questions tagged or ask your own question.