JavaScript正则表达式和子匹配项


70

g设置修饰符后,为什么Java子匹配项停止工作?

var text = 'test test test test';

var result = text.match(/t(e)(s)t/);
// Result: ["test", "e", "s"]

以上工作正常,result[1]is"e"result[2]is "s"

var result = text.match(/t(e)(s)t/g);
// Result: ["test", "test", "test", "test"]

上面的内容忽略了我的捕获组。以下是唯一有效的解决方案吗?

var result = text.match(/test/g);
for (var i in result) {
    console.log(result[i].match(/t(e)(s)t/));
}
/* Result:
["test", "e", "s"]
["test", "e", "s"]
["test", "e", "s"]
["test", "e", "s"]
*/

编辑:

我再次高兴地告诉您,十年后您现在可以执行此操作(.matchAll已添加到规范中)

let result = [...text.matchAll(/t(e)(s)t/g)];

Answers:


96

如您所知,如果设置了全局修饰符,则使用Stringmatch()函数将不会返回捕获的组。

在这种情况下,您将要使用一个RegExp对象并调用其exec()函数。Stringmatch()功能几乎与RegExpexec()功能相同……除了在这种情况下。如果设置了全局修饰符,则普通match()函数将不会返回捕获的组,而RegExpexec()函数会返回。(在此处以及其他地方注明。)

要记住的另一个要点是,exec()它不会在一个大数组中返回匹配项,它会一直返回匹配项,直到用完为止,在这种情况下,它将返回null

因此,例如,您可以执行以下操作:

var pattern = /t(e)(s)t/g;  // Alternatively, "new RegExp('t(e)(s)t', 'g');"
var match;    

while (match = pattern.exec(text)) {
    // Do something with the match (["test", "e", "s"]) here...
}

还有一点要注意的是,RegExp.prototype.exec()RegExp.prototype.test()执行上提供的字符串的正则表达式,返回的第一个结果。每个顺序调用都将RegExp.prototype.lastIndex根据字符串中的当前位置逐步更新结果集。

这是一个示例://请记住,示例和模式中有4个匹配项。lastIndex从0开始

pattern.test(text); // pattern.lastIndex = 4
pattern.test(text); // pattern.lastIndex = 9
pattern.exec(text); // pattern.lastIndex = 14
pattern.exec(text); // pattern.lastIndex = 19

// if we were to call pattern.exec(text) again it would return null and reset the pattern.lastIndex to 0
while (var match = pattern.exec(text)) {
    // never gets run because we already traversed the string
    console.log(match);
}

pattern.test(text); // pattern.lastIndex = 4
pattern.test(text); // pattern.lastIndex = 9

// however we can reset the lastIndex and it will give us the ability to traverse the string from the start again or any specific position in the string
pattern.lastIndex = 0;

while (var match = pattern.exec(text)) {
    // outputs all matches
    console.log(match);
}

您可以找到有关如何使用MDN上的RegExp对象的信息(具体来说,这函数的文档)。exec()


3
使用exec似乎不听g修饰符,但它支持子匹配/组。因此结果将是第一个匹配项(它基本上忽略了g修饰符)
乍得·希拉

2
不是最优雅的解决方案。我期待这样的输出:[[“ test”,“ e”,“ s”],[“ test”,“ e”,“ s”],[“ test”,“ e”,“ s” ],[“ test”,“ e”,“ s”]]
乍得·史基拉

3
对于其他碰到另一个问题的注意事项:如果.test()在此之前使用,请确保pattern.lastIndex = 0while循环之前使用重置lastIndex以获取所有匹配项
Iulian Onofrei 2014年

2
g标志不会被忽略。它必须在那里,否则您将陷入无限循环。在这里找到困难的方法:)
Sarsaparilla 2014年

1
明确addresssing另一个角落的情况:如果正则表达式有一个捕获组,但全球改性剂没有被使用,匹配()将返回第一个完整的匹配,然后匹配捕捉所有子。例如,'foobar'.match(/f(o)*(ba)/)将返回["fooba", "o", "ba"]
埃里克·阮

4

我很惊讶地发现我是第一个回答我十年前一直在寻找答案的人(答案尚不存在)。我还希望实际的规范作者会在我之前回答它;)。

.matchAll已被添加到一些浏览器中。

在现代javascript中,我们现在只需执行以下操作即可完成此操作。

let result = [...text.matchAll(/t(e)(s)t/g)];

.matchAll规格

.matchAll文档

现在,我维护一个同构javascript库,该库可以帮助处理许多此类字符串解析。您可以在这里查看:string-saw。使用命名捕获组时,它有助于简化.matchAll的使用。

一个例子是

saw(text).matchAll(/t(e)(s)t/g)

它将输出更加用户友好的匹配数组,如果您想花哨的话,可以将其放入命名的捕获组并获取对象数组。


1
语言不断发展。所以做的Javascript。很高兴成为这个华丽答案的第一个支持者。
КонстантинВан
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.