Andrew Axton
Andrew Axton

Reputation: 1030

Regex exec() isn't returning first occurrence in string, but returns subsequent matches

I'm trying to find all occurrences of colors in a string. If I have a string of 'red #fff green #000000' it will only match the second result, so green and #000000 in this case.

// csscolor is just json of all color names

const types = {
  hsl: new RegExp(/(hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\))/gi),
  rgb: new RegExp(/(rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}))\)/gi),
  hex: new RegExp(/(#[0-9a-f]{6}|#[0-9a-f]{3})/gi),
  keyword: new RegExp('\\b(' + Object.keys(csscolors).join('|') + ')\\b', 'gi')
};

const execRegex = (re, str) => {
  var match;
  while ((match = re.exec(str)) !== null) {
    console.log('regexp.lastIndex:', re.lastIndex, 
                'index:', match.index,
                'match[0]:', match[0]);
  }
}

const getMatches = (str) => ({
  hsl: types.hsl.test(str) ? execRegex(types.hsl, str) : null,
  rgb: types.rgb.test(str) ? execRegex(types.rgb, str) : null,
  hex: types.hex.test(str) ? execRegex(types.hex, str) : null,
  keyword: types.keyword.test(str) ? execRegex(types.keyword, str) : null
});

getMatches('red #fff green #000000');

output, missing red and #fff:

regexp.lastIndex: 22 
index: 15 
match[0]: #000000

regexp.lastIndex: 14 
index: 9 
match[0]: green

I've tested the regex's with match() and they seem to be working just fine, but match doesn't provide index's for multiple occurrences.

disclaimer: RegExp noob

Upvotes: 1

Views: 466

Answers (1)

Chris Anderson
Chris Anderson

Reputation: 8515

RegExp test and exec methods both use the RegExp lastIndex property, so when you call test, you're advancing past the first match, then finding the second with exec.

See this related Stack Overflow post: Why RegExp with global flag in Javascript give wrong results?

Upvotes: 3

Related Questions