So I have seen this happen in multiple projects and I have no idea what is going on here. Please help me demystify this.
Let's say you have a regex:
/^(hello|goodbye)?\s*world$/gi
And you want to test it against the following strings:
...
"hello world", // PASSES
"goodbye world", // PASSES
"hello goodbye", // FAILS
"world world" // FAILS
...
So you use the test method to evaluate the strings and whether or not they pass:
...
/^(hello|goodbye)?\s*world$/gi.test(searchTexts[0])
And you get the results you expect. However, now you need to assign the regex into a variable (you know, to reuse it):
...
const helloWorldRegex = /^(hello|goodbye)?\s*world$/gi
And all of a sudden, this doesn't work:
...
new RegExp(helloWorldRegex).test(searchTexts[0]) // fails
helloWorldRegex.test(searchTexts[0]) // fails
What gives? I DON'T KNOW
All I know is that THE HACK BELOW MAKES NO SENSE and I want an explanation:
...
const hackedRegex = new RegExp(/^(hello|goodbye)?\s*world$/gi)
searchTexts[0].match(hackedRegex)
const hackedRegexPasses = hackedRegex.test(searchTexts[0])
The code below is isomorphic and will work in the browser or in node, whichever you prefer or run here: https://jsfiddle.net/qLm5679z/72/
const poutput = (text) => {
if (typeof window !== 'undefined') {
if (!document.getElementById("out")) document.body.innerHTML = "<div id='out'></div>"
const outputEl = document.getElementById("out")
outputEl.innerHTML = `<p>${(Array.isArray(text) ? text : [text]).join('<br>')}</p>`
}
console.log(text)
}
const searchTexts = [
// Using the regex = /^(hello|goodbye)?\s*world$/gi
"hello world", // PASSES
"goodbye world", // PASSES
"hello goodbye", // FAILS
"world world" // FAILS
]
const assignedRegex = /^(hello|goodbye)?\s*world$/gi
const constructedRegex = new RegExp(/^(hello|goodbye)?\s*world$/gi)
const nonLiteral = new RegExp("^(hello|goodbye)?\\s*world$", "gi")
const nonGlobal = new RegExp("^(hello|goodbye)?\\s*world$", "i")
for (const search of searchTexts) {
const inlineRegexPasses = /^(hello|goodbye)?\s*world$/gi.test(search)
const assignedRegexPasses = assignedRegex.test(search)
const constructedRegexPasses = constructedRegex.test(search)
const nonLiteralRegexPasses = nonLiteral.test(search)
const nonGlobalRegexPasses = nonGlobal.test(search)
const hackedRegex = new RegExp(/^(hello|goodbye)?\s*world$/gi)
search.match(hackedRegex)
const hackedRegexPasses = hackedRegex.test(search)
poutput([
`${search}::inlineRegexPasses::${inlineRegexPasses ? 'PASS' : 'FAIL'}`,
`${search}::nonGlobalRegexPasses::${nonGlobalRegexPasses ? 'PASS' : 'FAIL'}`,
`${search}::hackedRegexPasses::${hackedRegexPasses ? 'PASS' : 'FAIL'}`,
`${search}::assignedRegexPasses::${assignedRegexPasses ? 'PASS' : 'FAIL'}`,
`${search}::constructedRegexPasses::${constructedRegexPasses ? 'PASS' : 'FAIL'}`,
`${search}::nonLiteralRegexPasses::${nonLiteralRegexPasses ? 'PASS' : 'FAIL'}`
])
}
CodePudding user response:
Answered here: Why does a RegExp with global flag give wrong results?
Short explanation: the "g" flag makes the RegExp
stateful and keeps the last index from the last match
/test
.
Also, calling the constructor with a literal makes it share the state of the literal. If you want the constructor to create a new RegExp
with a clean state, you need to call it with the string pattern and the flags parameters like so:
new RegExp("^(hello|goodbye)?\\s*world$", "i")
CodePudding user response:
I use https://regexr.com/ for problems with regex. I do not keep this information, when I need it, I use the validator that appears on this site.