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

var myString=“something format_abc”;//我要“abc”var arr=/(?:^|\s)format_(.*?)(?:\s|$)/.exec(myString);控制台日志(arr);//打印:[“format_abc”,“abc”]。。到现在为止,一直都还不错。控制台日志(arr[1]);//打印:未定义(???)控制台日志(arr[0]);//打印:format_undefined(!!!)

我做错了什么?


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

"date format_%A"

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


问题是console.log像printf语句一样接受其参数,因为我记录的字符串(“%a”)有一个特殊值,所以它试图查找下一个参数的值。


当前回答

正如@cms在ECMAScript(ECMA-262)中所说,您可以使用matchAll。它返回一个迭代器,并通过将其放入[…](扩展运算符)中转换为数组。(此正则表达式提取文件名的URL)

let text=`<a href=“http://myhost.com/myfile_01.mp4“>文件1</a><a href=”http://myhost.com/myfile_02.mp4“>文件2</a>`;let fileUrls=[…text.matchAll(/href=“(http://\/[^”]+\.\w{3})\”/g)].map(r=>r[1]);console.log(fileUrls);

其他回答

只有当你有一对括号时才实用的一行:

while ( ( match = myRegex.exec( myStr ) ) && matches.push( match[1] ) ) {};

正如@cms在ECMAScript(ECMA-262)中所说,您可以使用matchAll。它返回一个迭代器,并通过将其放入[…](扩展运算符)中转换为数组。(此正则表达式提取文件名的URL)

let text=`<a href=“http://myhost.com/myfile_01.mp4“>文件1</a><a href=”http://myhost.com/myfile_02.mp4“>文件2</a>`;let fileUrls=[…text.matchAll(/href=“(http://\/[^”]+\.\w{3})\”/g)].map(r=>r[1]);console.log(fileUrls);

不需要调用exec方法!您可以直接对字符串使用“match”方法。别忘了括号。

var str = "This is cool";
var matches = str.match(/(This is)( cool)$/);
console.log( JSON.stringify(matches) ); // will print ["This is cool","This is"," cool"] or something like that...

位置0有一个包含所有结果的字符串。位置1的第一个匹配项用括号表示,位置2的第二个匹配项在括号中隔离。嵌套的括号很棘手,所以要小心!

更新:2019-09-10

旧的迭代多个匹配的方法不是很直观。这导致了String.prototype.matchAll方法的提出。这种新方法在ECMAScript 2020规范中。它为我们提供了一个干净的API并解决了多个问题。自从Chrome 73+/Node 12+和Firefox 67+之后,它就出现在主流浏览器和JS引擎中。

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

const string=“something format_abc”;常量regexp=/(?:^|\s)format_(.*?)(?:\s|$)/g;const matches=string.matchAll(正则表达式);for(匹配的常量匹配){console.log(匹配);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]);
}

与此同时,虽然这一提议得到了更广泛的支持,但您可以使用官方垫片包。

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

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;
  }
}

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

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

我也很高兴看到,在提案的讨论中甚至提到了这个StackOverflow问题。

原始答案

您可以这样访问捕获组:

var myString=“something format_abc”;var myRegexp=/(?:^|\s)format_(.*?)(?:\s|$)/g;var myRegexp=新RegExp(“(?:^|\s)format_(.*?)(?:\s|$)”,“g”);var matches=myRegexp.exec(myString);console.log(匹配[1]);//abc

如果有多个匹配项,您可以对它们进行迭代:

var myString=“something format_abc”;var myRegexp=新RegExp(“(?:^|\s)format_(.*?)(?:\s|$)”,“g”);match=myRegexp.exec(myString);while(匹配!=null){//匹配的文本:匹配[0]//匹配开始:match.index//捕获组n:匹配[n]console.log(匹配[0])match=myRegexp.exec(myString);}

单线解决方案:

const matches = (text,regex) => [...text.matchAll(regex)].map(([match])=>match)

因此您可以使用这种方式(必须使用/g):

matches("something format_abc", /(?:^|\s)format_(.*?)(?:\s|$)/g)

结果:

[" format_abc"]