一尘不染

如何访问 JavaScript 正则表达式中的匹配组?

javascript

我想使用正则表达式匹配字符串的一部分,然后访问带括号的子字符串:

    var myString = "something format_abc"; // I want "abc"

    var arr = /(?:^|\s)format_(.*?)(?:\s|$)/.exec(myString);

    console.log(arr);     // Prints: [" format_abc", "abc"] .. so far so good.
    console.log(arr[1]);  // Prints: undefined  (???)
    console.log(arr[0]);  // Prints: format_undefined (!!!)

我究竟做错了什么?


我发现上面的正则表达式代码没有任何问题:我正在测试的实际字符串是这样的:

"date format_%A"

报告“%A”未定义似乎是一种很奇怪的行为,但它与这个问题没有直接关系,所以我新开了一个,为什么在 JavaScript 中匹配的子字符串返回“未定义”?)*.


问题是它console.log把它的参数当作一个printf语句,因为我记录的字符串 ( "%A") 有一个特殊的值,它试图找到下一个参数的值。


阅读 128

收藏
2022-02-22

共1个答案

一尘不染

您可以像这样访问捕获组:

var myString = "something format_abc";
var myRegexp = /(?:^|\s)format_(.*?)(?:\s|$)/g;
var myRegexp = new RegExp("(?:^|\s)format_(.*?)(?:\s|$)", "g");
var match = myRegexp.exec(myString);
console.log(match[1]); // abc

如果有多个匹配项,您可以遍历它们:

var myString = "something format_abc";
var myRegexp = new RegExp("(?:^|\s)format_(.*?)(?:\s|$)", "g");
match = myRegexp.exec(myString);
while (match != null) {
  // matched text: match[0]
  // match start: match.index
  // capturing group n: match[n]
  console.log(match[0])
  match = myRegexp.exec(myString);
}

编辑:2019-09-10

如您所见,迭代多个匹配项的方式不是很直观。这导致了该String.prototype.matchAll方法的提议。这种新方法预计将在ECMAScript 2020 规范中发布。它为我们提供了一个干净的 API 并解决了多个问题。它已开始登陆主流浏览器和 JS 引擎,如Chrome 73+ / Node 12+和 Firefox 67+。

该方法返回一个迭代器,用法如下:

const string = "something format_abc";
const regexp = /(?:^|\s)format_(.*?)(?:\s|$)/g;
const matches = string.matchAll(regexp);

for (const match of matches) {
  console.log(match);
  console.log(match.index)
}

因为它返回一个迭代器,我们可以说它是惰性的,这在处理特别大量的捕获组或非常大的字符串时很有用。但是如果需要,可以使用扩展语法Array.from方法轻松地将结果转换为数组:

function getFirstGroup(regexp, str) {
  const array = [...str.matchAll(regexp)];
  return array.map(m => m[1]);
}

// or:
function getFirstGroup(regexp, str) {
  return Array.from(str.matchAll(regexp), m => m[1]);
}

同时,虽然这个提议得到了更广泛的支持,但你可以使用官方的 shim 包

此外,该方法的内部工作很简单。使用生成器函数的等效实现如下:

function* matchAll(str, regexp) {
  const flags = regexp.global ? regexp.flags : regexp.flags + "g";
  const re = new RegExp(regexp, flags);
  let match;
  while (match = re.exec(str)) {
    yield match;
  }
}

创建原始正则表达式的副本;lastIndex这是为了避免在进行多重匹配时由于属性的突变而产生的副作用。

此外,我们需要确保正则表达式具有全局标志以避免无限循环。

2022-02-22