Ad

Regex Exec() Isn't Returning First Occurrence In String, But Returns Subsequent Matches

- 1 answer

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

Ad

Answer

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?

Ad
source: stackoverflow.com
Ad