[英]Template literal inside of the RegEx
我试图在 RegEx 中放置一个模板文字,但它不起作用。 然后我制作了一个变量regex
来保存我的regex
,但它仍然没有给我想要的结果。
但是,如果我单独console.log(regex)
,我确实收到了所需的 RegEx,例如/.+?(?=location)/i
, /.+?(?=date)/i
等等,但是一旦我将regex
放在.replace
它似乎不起作用
function validate (data) {
let testArr = Object.keys(data);
errorMessages.forEach((elem, i) => {
const regex = `/.+?(?=${elem.value})/i`;
const a = testArr[i].replace(regex, '');
})
}
您的regex
变量是String 。 要使其成为 RegExp,请使用RegExp
构造函数:
const regex = new RegExp(String.raw`pattern_as_in_regex_literal_without_delimiters`)
例如,像/<\\d+>/g
这样的正则表达式文字可以重写为
const re = RegExp(String.raw`<\d+>`, 'g') // One \ is a literal backslash
const re = RegExp(`<\\d+>`, 'g') // Two \ are required in a non-raw string literal
要插入变量,您可以使用
const digits = String.raw`\d+`;
const re = RegExp(`<${digits}>`, 'g')
为了解决您的问题,您可以使用
const regex = new RegExp(`.+?(?=${elemvalue.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')})`, "i");
此外,最好在正则表达式中转义变量部分,以便所有特殊的正则表达式元字符都被视为文字。
const s = "final (location)"; const elemvalue = "(location)"; const regex = new RegExp(`.+?(?=${elemvalue.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&')})`, "i"); // console.log(regex); // /.+?(?=\\(location\\))/i // console.log(typeof(regex)); // object let a = s.replace(regex, ''); console.log(a);
更高级的模板文字形式是Tagged templates 。 -MDN
const escape = s => `${s}`.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, "\\\\$&"); const regex = ({ // (1) raw: [part, ...parts] }, ...subs) => new RegExp( subs.reduce( // (2) (result, sub, i) => `${result}${escape(sub)}${parts[i]}`, part ) ); const t1 = `d`; const r1 = regex `^ab(c${t1}e)`; // (3) // /^ab(cde)/ console.log('r2.test(`abc${t1}e`); ➜', r1.test(`abc${t1}e`)); // true // Check for proper escaped special chars const t2 = `(:?bar)\\d{2}`; const r2 = regex `foo${t2}`; // /foo\\(:\\?bar\\)d\\{2\\}/ ➜ t2 is escaped! console.log('r2.test(`foo${t2}`); ➜', r2.test(`foo${t2}`)); // true console.log('r2.test(`foo\\(:\\?bar\\)d\\{2\\}`); ➜', r2.test(`foo\\(:\\?bar\\)d\\{2\\}`)); // true console.log('r2.test(`foobar11`); ➜', r2.test(`foobar11`)); // false console.log(r2);
定义一个“标签函数”(名称无关紧要),例如regex()
) 从模板文字字符串构建我们的正则表达式( return new RegExp()
)。
提示:标签函数甚至不需要返回字符串!
第一个参数parts
是一个string[]
(注入变量之间的段)。
a${..}bc${..}de
➜ ['a', 'bc', 'de'] = parts
提示: tag 函数的第一个参数上可用的特殊
raw
属性允许您在输入原始字符串时访问它们,而无需处理转义序列。
这就是我们需要的! 我们可以解构第一个参数,并拆分出parts
的第一part
。 这是我们的第一个 regExp result
段。
subs...
是来自注入变量的替换值:
..${'x'}..${'y'}..
➜ ['x', 'y']
循环...subs
并连接正则表达式字符串:
escape()
当前stubs[i]
特殊字符(这会破坏最终表达式)。stubs[i]
并在其间添加parts[i]
。 为模板文字字符串添加前缀,使用regex `...`
并返回一个新的RegExp
:
const r1 = regex `^ab(c${t1}e)`
➜ /^ab(cde)/
到目前为止, regex()
所有模板变量..${..}..${..}..
作为字符串处理。 (调用.toString()
)
regex `X${new Date()}X` // /X7\.12\.2020X/
regex `X${{}}X` // /X\[object Object\]X/
regex `X${[1, 'b', () => 'c']}X` // /X1,b,\(\) => 'c'X/
即使这是预期的行为,我们也不能在所有地方都使用它。
如何在“标记模板”中连接正则表达式?
用例:您想要一个“RegExp 工厂类”从较小的部分构建更复杂的表达式。 例如,用于解析/验证类似 javascript MIME 类型的
Content-Type
标头值的 RegExp。 语法:media-type = type "/" subtype
。 这就是我们想要找到的:
*/*
application/*
,application/javascript
,application/ecmascript
,text/*
,text/javascript
,text/ecmascript
const group = (name, regExp) => regex`(?<${name}>${regExp})`;
const rTypeGroup = group(`type`, `(text|application|\*)+?`);
// /(?<type>\(text\|application\|\*\)\+\?)/
const rSubGroup = group(`sub`, `((java|ecma)+?script|\*)+?`);
// /(?<sub>\(\(java\|ecma\)\+\?script\|\*\)\+\?)/
// .. and test
rTypeGroup.test(`application`); // false !!!
rTypeGroup.test(`\(text\|application\|\*\)\+\?`); // true !!!
由于regex()
转义了所有替换,因此我们的捕获组的主体在字面上匹配。 对于某些类型,我们可以修改regex()
并跳过escape()
。 现在我们可以传递 RegEx 实例并跳过escape()
。
const escape = s => `${s}`.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, "\\\\$&"); const regex = ({ raw: [part, ...parts] }, ...subs) => new RegExp( // skip escape() subs.reduce( // ┏━━━━━━━━━┻━━━━━━━━━┓ (result, sub, i) => `${result}${sub instanceof RegExp ? sub.source : escape(sub)}${parts[i]}`, part ) ); const group = (name, regExp) => regex `(?<${name}>${regExp})`; // RegEx // ┏━━━━━━━━━━━┻━━━━━━━━━━━┓ const rTypeGroup = group(`type`, /(text|application|\\*)+?/); // /(?<type>(text|application|\\*)+?)/ const rSubGroup = group(`sub`, /((java|ecma)+?script|\\*)+?/); // /(?<sub>((java|ecma)+?script|\\*)+?)/ // Type console.log('rTypeGroup.test(`*`); ➜', rTypeGroup.test(`*`)); // true console.log('rTypeGroup.test(`text`); ➜', rTypeGroup.test(`text`)); // true console.log('rTypeGroup.exec(`*`).groups.type; ➜', rTypeGroup.exec(`*`).groups.type); // '*' console.log('rTypeGroup.exec(`text`).groups.type; ➜', rTypeGroup.exec(`text`).groups.type); // 'text' // SubType console.log('rSubGroup.test(`*`); ➜', rSubGroup.test(`*`)); // true console.log('rSubGroup.test(`javascript`); ➜', rSubGroup.test(`javascript`)); // true console.log('rSubGroup.test(`ecmascript`); ➜', rSubGroup.test(`ecmascript`)); // true console.log('rSubGroup.exec(`*`).groups.sub; ➜', rSubGroup.exec(`*`).groups.sub); // '*' console.log('rSubGroup.exec(`javascript`).groups.sub; ➜', rSubGroup.exec(`javascript`).groups.sub); // 'javascript'
我们现在可以决定是否应该转义 var。 由于我们忽略了任何量词和标志,我们的小组还验证了ABCtextDEF
、 12texttext
、...。
现在我们可以捆绑多个 RegEx:
// '/' would be escaped! RegEx needed..
// ┏━┻━┓
const rMediaTypeGroup = group(`mediaType`, regex `${rTypeGroup}${/\//}${rSubGroup}`);
// /(?<mediaType>(?<type>(text|application|\*)+?)\/(?<sub>((java|ecma)+?script|\*)+?))/
rMediaTypeGroup.test(`text/javascript`)); // true
const escape = s => `${s}`.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, "\\\\$&"); const regex = ({ raw: [part, ...parts] }, ...subs) => new RegExp( subs.reduce( (result, sub, i) => `${result}${sub instanceof RegExp ? sub.source : escape(sub)}${parts[i]}`, part ) ); const group = (name, regExp) => regex `(?<${name}>${regExp})`; const rTypeGroup = group(`type`, /(text|application|\\*)+?/); const rSubGroup = group(`sub`, /((java|ecma)+?script|\\*)+?/); // '/' would be escaped! RegEx needed.. // ┏━┻━┓ const rMediaTypeGroup = group(`mediaType`, regex `${rTypeGroup}${/\\//}${rSubGroup}`); // /(?<mediaType>(?<type>(text|application|\\*)+?)\\/(?<sub>((java|ecma)+?script|\\*)+?))/ console.log('rMediaTypeGroup.test(`*/*`); ➜', rMediaTypeGroup.test(`*/*`)); // true console.log('rMediaTypeGroup.test(`**/**`); ➜', rMediaTypeGroup.test(`**/**`)); // true console.log('rMediaTypeGroup.test(`text/javascript`); ➜', rMediaTypeGroup.test(`text/javascript`)); // true console.log('rMediaTypeGroup.test(`1text/javascriptX`); ➜', rMediaTypeGroup.test(`1text/javascriptX`)); // true console.log('rMediaTypeGroup.test(`*/java`); ➜', rMediaTypeGroup.test(`*/java`)); // true console.log('rMediaTypeGroup.test(`text/X`); ➜', rMediaTypeGroup.test(`text/X`)); // false console.log('rMediaTypeGroup.test(`/*`); ➜', rMediaTypeGroup.test(`/*`)); // false
在初始化之前必须知道所有标志。 您可以阅读它们( /xx/gm.flags
, /xx/gm.multiline
, /xx/i.ignoreCase
,..),但没有设置器。
返回
new Regex()
的标记模板函数(例如regex()
new Regex()
需要知道所有标志。
本节演示了如何处理标志的 3 种替代方法。
${templateVariable}
➜ 不推荐!class RegExp
Proxy()
作为标志${templateVariable}
传递。 ➜ 不推荐! 像其他替换变量一样的线程标志。 我们需要检查最后一个变量是否类似于标志( g
, mi
,..)并将其与subs
分开......
const escape = s => `${s}`.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, "\\\\$&"); const regex = ({ raw: [part, ...parts], // super verbose... splitFlags = ({ // destruct subs[] Array (arrays are objects!) length: l, // destruct subs[].length to l iEnd = l - 1, // last array index to iEnd [iEnd]: sub, // subs[subs.length - i] to sub ...subs // subs[0...n-1] to subs }) => [ // returns RegEx() constr. params: [flags, concat regex string] // ┏━━━━━━━━ all chars of sub flag-like? ━━━━━━━━━┓ ┏━ flags ┏━ re-add last sub and set flags: undefined [...sub].every(f => ['s', 'g', 'i', 'm', 'y', 'u'].includes(f)) ? sub : !(subs[iEnd] = sub) || undefined, Object.values(subs).reduce( // concat regex string (result, sub, i) => `${result}${escape(sub)}${parts[i]}`, part ) ] }, ...subs) => new RegExp(...splitFlags(subs).reverse()); const r1 = regex `^foo(${`bar`})${'i'}`; console.log('r1:', r1, 'flags:', r1.flags); // /^foo(bar)/i ['i' flag] const r2 = regex `^foo(${`bar`})${'mgi'}`; console.log('r2:', r2, 'flags:', r2.flags); // /^foo(bar)/gim ['gim' flag] // invalid flag 'x' ━━━━┓ const r3 = regex `^foo(${`bar`})${'x'}`; console.log('r3:', r3, 'flags:', r3.flags); // /^foo(bar)x/ [no flags] // invalid flag 'z' ━━━━┓ const r4 = regex `^foo(${`bar`})${'gyzu'}`; console.log('r4:', r4, 'flags:', r4.flags); // /^foo(bar)gyzu/ [no flags]
代码看起来超级冗长,并且从外部混淆标志逻辑和替换逻辑并不明显。 如果最后一个变量被错误地确定为flag-like ,它也会破坏最终的正则表达式。
我们寻找电话类型,例如
i-phone
、a-phone
、...
const rPhoneA = regex `${`a`}-phone`; //
console.log('rPhoneA:', rPhoneA, 'flags:', rPhoneA.flags); // /a-phone/ [no flags]
const rPhoneI = regex `${`i`}-phone`;
console.log('rPhoneI:', rPhoneI, 'flags:', rPhoneI.flags); // /(?:)/i ['i' flag]
console.log('rPhoneA.test(`a-phone`); ➜', rPhoneA.test(`a-phone`)); // true
console.log('rPhoneA.test(`i-phone`); ➜', rPhoneA.test(`i-phone`)); // false
console.log('rPhoneI.test(`a-phone`); ➜', rPhoneI.test(`a-phone`)); // true
console.log('rPhoneI.test(`i-phone`); ➜', rPhoneI.test(`i-phone`)); // true
...所有手机都是i-phones
! 因为i
是一个类似标志的替代品,并且已从subs...
数组中删除,该数组现在为空[]
。 reduce()
返回一个空字符串''
并且new RegExp('', 'i')
添加一个空的非捕获组: (?:)
。
class RegExp
我们可以从RegExp
扩展并添加getter方法来设置标志。 让它们自动返回,这样我们就可以将它们链接起来。 我们甚至可以添加一个_
“清除标志”方法。
这很好用,但也有效果,每个添加/删除的标志都会导致TRegExp
的新克隆。 如果我们构建“静态”(可缓存)表达式,这对您来说可能没问题。
const escape = s => `${s}`.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, "\\\\$&"); class TRegExp extends RegExp { constructor(...args) { super(...args); // Clear all flags Object.defineProperty(this, '_', { get() { return this.flags.length ? new TRegExp(this.source) : this; }, enumerable: false }); // A: define getters for all flags ['g', 'i', 'm', 'u', 'y'].reduce((my, flag) => Object.defineProperty(my, flag, { get() { // clone this on flags change ━━┓ return my.flags.includes(flag) ? my : new TRegExp(my.source, `${my.flags}${flag}`); }, // return this ━━┛ enumerable: false }), this); } // B: Define getters for each flag individually // get g() { // return this.flags.includes('g') ? this : new TRegExp(this.source, `${this.flags}g`); // } } const regex = ({raw: [part, ...parts]}, ...subs) => new TRegExp( subs.reduce( // ┣━━ TRegExp() (result, sub, i) => `${result}${subs instanceof TRegExp ? sub.source : escape(sub)}${parts[i]}`, part ) ); console.log('TRegExp +flags:', regex `foo(bar)`.g); // /foo(bar)/g console.log('TRegExp +flags:', regex `foo(bar)`.im); // /foo(bar)/im console.log('TRegExp +flags:', regex `foo(bar)`.gi_.y); // /foo(bar)/y // ┗━━━━━┻━━━━ ( + 'g', + 'i', - 'gi', + 'y') const group = regex `(?<foo>(:?bar)\\d{2})`.gi; const t = `a bar12 bar0 bar13 bar-99 xyz BaR14 bar15 abc`; console.log([...t.matchAll(group)].map(m => m.groups.foo)); // ["bar12", "bar13", "BaR14", "bar15"]
Proxy()
作为标志您可以代理“标记模板函数”,例如regex()
并拦截其上的任何get()
。 代理可以解决很多问题,但也会导致很大的混乱。 您可以重新编写整个函数并完全改变初始行为。
const escape = s => `${s}`.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, "\\\\$&"); const _regex = (flags, {raw: [part, ...parts]}, ...subs) => new RegExp( subs.reduce( (result, sub, i) => `${result}${sub instanceof RegExp ? sub.source : escape(sub)}${parts[i]}`, part ), flags ); // ┗━━━ flags const regex = new Proxy(_regex.bind(undefined, ''), { get: (target, property) => _regex.bind(undefined, property) }); console.log('Proxy +flags:', regex.gi `foo(bar)`); // /foo(bar)/gi const r = /(:?bar)\\d{2}/; // matches: 'bar' + digit + digit ➜ 'bar12', 'abar123',.. const t = `(:?bar)\\d{2}`; // template literal with regExp special chars console.log('Proxy +flags:', regex.gi `(foo${r})`); // /(foo(:?bar)\\d{2})/gi console.log('Proxy +flags:', regex.gi `(foo${t})`); // /(foo\\(:\\?bar\\)d\\{2\\})/gi // flags ━┻━┛
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.