JavaScript 正则表达式和子匹配

IT技术 javascript regex
2021-01-13 14:23:40

为什么 Javascript 子匹配在设置g修饰符后停止工作

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"]
*/

编辑:

我再次高兴地告诉你,10 年后你现在可以做到这一点(.matchAll 已添加到规范中)

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

如您所见,如果设置了全局修饰符,usingStringmatch()函数将不会返回捕获的组。

在这种情况下,您可能想要使用一个RegExp对象并调用它的exec()函数。String'smatch()RegExp's 的exec()功能几乎相同……除了像这样的情况。如果设置了 global 修饰符,则普通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()

我知道老问题,但我最近需要这个,我想出了这个:RegExp.prototype.execAll = function(s) { var r = [],m; while(m = this.exec(s)) r.push(m); return r; }. 有了它,您可以:/t(e)(s)t/.matchAll("test")并获得@ChadScira 正在寻找的结果。
2021-03-17 14:23:40
不是最优雅的解决方案。我期待的输出有点像这样: [ [“test”, “e”, “s”], [“test”, “e”, “s”], [“test”, “e”, “s” ], ["测试", "e", "s"] ]
2021-03-25 14:23:40
g 标志不会被忽略。它需要在那里,否则你会得到一个无限循环。在这里找到了艰难的方法:)
2021-03-26 14:23:40
其他人遇到另一个问题的注意事项:如果.test()在它之前使用,请确保pattern.lastIndex = 0while循环之前使用重置 lastIndex以获取所有匹配项
2021-03-27 14:23:40
使用 exec 似乎不听 g 修饰符,但它支持子匹配/组。所以结果将是第一个匹配(它基本上忽略了 g 修饰符)
2021-04-02 14:23:40

我很惊讶地看到我是第一个用我 10 年前寻找的答案来回答这个问题的人(答案还不存在)。我也希望实际的规范作者会在我之前回答它;)。

.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)

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

String#matchAll 返回一个迭代器,而不是一个数组。
2021-03-16 14:23:40
语言在进化。所以做的Javascript很高兴成为这个华丽答案的第一个支持者。
2021-04-10 14:23:40