如何在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.logprintf语句一样接受它的参数,并且由于我记录的字符串("%A")有一个特殊的值,它试图找到下一个参数的值。

989399 次浏览

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

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

正如你所看到的,迭代多个匹配的方式不是很直观。这导致了#0方法的提出。这个新方法预计将在ECMAScript 2020规范中发布。它为我们提供了一个干净的API并解决了多个问题。它已经开始作为Chrome73+/节点12+和Firefox 67+登陆主要浏览器和JS引擎。

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

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

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

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

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属性的突变而产生的副作用。

此外,我们需要确保regexp具有全球标志以避免无限循环。

我也很高兴看到这个StackOverflow问题在讨论该提案中被引用。

使用您的代码:

console.log(arr[1]);  // prints: abcconsole.log(arr[0]);  // prints:  format_abc

编辑:Safari3,如果重要的话。

var myString = "something format_abc";var arr = myString.match(/\bformat_(.*?)\b/);console.log(arr[0] + " " + arr[1]);

\b并不完全相同。(它适用于--format_foo/,但不适用于format_a_b)但我想展示您的表达式的替代方案,这很好。当然,match调用是重要的。

您的代码适用于我(Mac上的FF3),即使我同意PhiLo的正则表达式可能应该是:

/\bformat_(.*?)\b/

(但是,当然,我不确定,因为我不知道regex的上下文。

您的语法可能不是最好的保留。FF/Gecko将RegExp定义为Function的扩展。
(FF2达到了typeof(/pattern/) == 'function'

这似乎是FF特有的IE、Opera和Chrome都为它抛出异常。

取而代之的是,使用之前其他人提到的方法:RegExp#execString#match
结果相同:

var regex = /(?:^|\s)format_(.*?)(?:\s|$)/;var input = "something format_abc";
regex(input);        //=> [" format_abc", "abc"]regex.exec(input);   //=> [" format_abc", "abc"]input.match(regex);  //=> [" format_abc", "abc"]

以下是一个方法,您可以使用它来获取每个匹配的第0个捕获组:

function getMatches(string, regex, index) {index || (index = 1); // default to the first capturing groupvar matches = [];var match;while (match = regex.exec(string)) {matches.push(match[index]);}return matches;}

// Example :var myString = 'something format_abc something format_def something format_ghi';var myRegEx = /(?:^|\s)format_(.*?)(?:\s|$)/g;
// Get an array containing the first capturing group for every matchvar matches = getMatches(myString, myRegEx, 1);
// Log resultsdocument.write(matches.length + ' matches found: ' + JSON.stringify(matches))console.log(matches);

只有当您有一对括号时才实用的单行线:

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

关于上面的多匹配括号示例,在没有得到我想要的东西之后,我在这里寻找答案:

var matches = mystring.match(/(?:neededToMatchButNotWantedInResult)(matchWanted)/igm);

在看了上面的使用这时和. ush()的稍微复杂的函数调用之后,我意识到这个问题可以用mystring.replace()非常优雅地解决(替换不是重点,甚至还没有完成,第二个参数的CLEAN,内置递归函数调用选项是!):

var yourstring = 'something format_abc something format_def something format_ghi';
var matches = [];yourstring.replace(/format_([^\s]+)/igm, function(m, p1){ matches.push(p1); } );

在这之后,我想我再也不会用. match()来做任何事情了。

/*Regex function for extracting object from "window.location.search" string.*/
var search = "?a=3&b=4&c=7"; // Example search string
var getSearchObj = function (searchString) {
var match, key, value, obj = {};var pattern = /(\w+)=(\w+)/g;var search = searchString.substr(1); // Remove '?'
while (match = pattern.exec(search)) {obj[match[0].split('=')[0]] = match[0].split('=')[1];}
return obj;
};
console.log(getSearchObj(search));

function getMatches(string, regex, index) {index || (index = 1); // default to the first capturing groupvar matches = [];var match;while (match = regex.exec(string)) {matches.push(match[index]);}return matches;}

// Example :var myString = 'Rs.200 is Debited to A/c ...2031 on 02-12-14 20:05:49 (Clear Bal Rs.66248.77) AT ATM. TollFree 1800223344 18001024455 (6am-10pm)';var myRegEx = /clear bal.+?(\d+\.?\d{2})/gi;
// Get an array containing the first capturing group for every matchvar matches = getMatches(myString, myRegEx, 1);
// Log resultsdocument.write(matches.length + ' matches found: ' + JSON.stringify(matches))console.log(matches);

function getMatches(string, regex, index) {index || (index = 1); // default to the first capturing groupvar matches = [];var match;while (match = regex.exec(string)) {matches.push(match[index]);}return matches;}

// Example :var myString = 'something format_abc something format_def something format_ghi';var myRegEx = /(?:^|\s)format_(.*?)(?:\s|$)/g;
// Get an array containing the first capturing group for every matchvar matches = getMatches(myString, myRegEx, 1);
// Log resultsdocument.write(matches.length + ' matches found: ' + JSON.stringify(matches))console.log(matches);

最后但并非最不重要的是,我发现了一行对我来说运行良好的代码(JS ES6):

let reg = /#([\S]+)/igm; // Get hashtags.let string = 'mi alegría es total! ✌🙌\n#fiestasdefindeaño #PadreHijo #buenosmomentos #france #paris';
let matches = (string.match(reg) || []).map(e => e.replace(reg, '$1'));console.log(matches);

这将返回:

['fiestasdefindeaño', 'PadreHijo', 'buenosmomentos', 'france', 'paris']

无需调用exec方法!您可以直接在字符串上使用“匹配”方法。只是不要忘记括号。

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的第二个匹配项在括号中隔离。嵌套括号很棘手,所以要小心!

本答案中使用的术语:

  • 匹配表示对字符串运行RegEx模式的结果,如下所示:someString.match(regexPattern)
  • 匹配模式表示输入字符串的所有匹配部分,它们都位于匹配数组中。这些都是输入字符串中模式的实例。
  • 匹配组表示要捕获的所有组,在RegEx模式中定义。(括号内的模式,如下所示:/format_(.*?)/g,其中(.*?)将是匹配的组。)这些驻留在匹配模式中。

特性介绍

要访问匹配组,在每个匹配模式中,您需要一个函数或类似的东西来迭代匹配。正如许多其他答案所显示的那样,您可以通过多种方式做到这一点。大多数其他答案都使用if循环来迭代所有匹配模式,但我认为我们都知道这种方法的潜在危险。有必要与new RegExp()匹配,而不仅仅是模式本身,这只在评论中提到。这是因为.exec()方法的行为类似于发生器函数-每次有比赛它就会停下来,但保留其.lastIndex以在下一个.exec()调用时从那里继续。

代码示例

下面是一个函数searchString的示例,它返回所有匹配模式中的Array,其中每个match都是一个Array,包含所有匹配组。我提供了同时使用Array.prototype.map()函数和更高性能的方式的示例,而不是使用年间循环-使用普通的for循环。

简洁的版本(更少的代码,更多的语法糖)

这些性能较差,因为它们基本上实现了forEach循环而不是更快的for循环。

// Concise ES6/ES2015 syntaxconst searchString =(string, pattern) =>string.match(new RegExp(pattern.source, pattern.flags)).map(match =>new RegExp(pattern.source, pattern.flags).exec(match));
// Or if you will, with ES5 syntaxfunction searchString(string, pattern) {return string.match(new RegExp(pattern.source, pattern.flags)).map(match =>new RegExp(pattern.source, pattern.flags).exec(match));}
let string = "something format_abc",pattern = /(?:^|\s)format_(.*?)(?:\s|$)/;
let result = searchString(string, pattern);// [[" format_abc", "abc"], null]// The trailing `null` disappears if you add the `global` flag

高性能版本(更多代码,更少语法糖)

// Performant ES6/ES2015 syntaxconst searchString = (string, pattern) => {let result = [];
const matches = string.match(new RegExp(pattern.source, pattern.flags));
for (let i = 0; i < matches.length; i++) {result.push(new RegExp(pattern.source, pattern.flags).exec(matches[i]));}
return result;};
// Same thing, but with ES5 syntaxfunction searchString(string, pattern) {var result = [];
var matches = string.match(new RegExp(pattern.source, pattern.flags));
for (var i = 0; i < matches.length; i++) {result.push(new RegExp(pattern.source, pattern.flags).exec(matches[i]));}
return result;}
let string = "something format_abc",pattern = /(?:^|\s)format_(.*?)(?:\s|$)/;
let result = searchString(string, pattern);// [[" format_abc", "abc"], null]// The trailing `null` disappears if you add the `global` flag

我还没有将这些替代方案与之前在其他答案中提到的替代方案进行比较,但我怀疑这种方法的性能和故障安全性不如其他方法。

使用es2018,您现在可以使用命名组String.match(),使您的正则表达式更明确地说明它试图做什么。

const url ='https://stackoverflow.com/questions/432493/how-do-you-access-the-matched-groups-in-a-javascript-regular-expression?some=parameter';const regex = /(?<protocol>https?):\/\/(?<hostname>[\w-\.]*)\/(?<pathname>[\w-\./]+)\??(?<querystring>.*?)?$/;const { groups: segments } = url.match(regex);console.log(segments);

你会得到类似

{协议:"https",主机名:"stackoverflow.com",路径名:"问题/432493/如何做你访问匹配的组在一个javascript正则表达式",查询字符串:"一些=参数"}

您实际上不需要显式循环来解析多个匹配项-将替换函数作为第二个参数传递,如下所述:#0

var str = "Our chief weapon is {1}, {0} and {2}!";var params= ['surprise', 'fear', 'ruthless efficiency'];var patt = /{([^}]+)}/g;
str=str.replace(patt, function(m0, m1, position){return params[parseInt(m1)];});
document.write(str);

m0参数表示完全匹配的子字符串{0}{1}等。m1表示第一个匹配组,即正则表达式中括号中包含的部分,第一个匹配的部分为0position是找到匹配组的字符串中的起始索引-在这种情况下未使用。

#0(参见第3阶段草案/2018年12月7日提案),简化了对匹配对象中所有组的访问(注意Group 0是整个匹配,而其他组对应于模式中的捕获组):

使用matchAll,您可以避免while循环和exec/g…相反,通过使用matchAll,您可以获得一个迭代器,您可以将其与更方便的#5阵列扩展#6结构一起使用

此方法产生类似于C#中的Regex.Matches、Python中的re.finditer、PHP中的preg_match_all的输出。

请参阅JS演示(在GoogleChrome73.0.3683.67(官方版本)中测试,beta(64位)):

var myString = "key1:value1, key2-value2!!@key3=value3";var matches = myString.matchAll(/(\w+)[:=-](\w+)/g);console.log([...matches]); // All match with capturing group values

console.log([...matches])显示

在此处输入图片描述

您还可以使用以下命令获取匹配值或特定组值

let matchData = "key1:value1, key2-value2!!@key3=value3".matchAll(/(\w+)[:=-](\w+)/g)var matches = [...matchData]; // Note matchAll result is not re-iterable
console.log(Array.from(matches, m => m[0])); // All match (Group 0) values// => [ "key1:value1", "key2-value2", "key3=value3" ]console.log(Array.from(matches, m => m[1])); // All match (Group 1) values// => [ "key1", "key2", "key3" ]

:查看浏览器兼容性详细信息。

我们可以通过使用反斜杠后跟匹配组的编号来访问正则表达式中的匹配组:

/([a-z])\1/

在第一组([a-z])表示的代码\1中匹配

获取所有组发生次数

let m=[], s = "something format_abc  format_def  format_ghi";
s.replace(/(?:^|\s)format_(.*?)(?:\s|$)/g, (x,y)=> m.push(y));
console.log(m);

如果你和我一样,希望regex会返回一个像这样的对象:

{match: '...',matchAtIndex: 0,capturedGroups: [ '...', '...' ]}

然后从下面剪掉函数

/*** @param {string | number} input*          The input string to match* @param {regex | string}  expression*          Regular expression* @param {string} flags*          Optional Flags** @returns {array}* [{match: '...',matchAtIndex: 0,capturedGroups: [ '...', '...' ]}]*/function regexMatch(input, expression, flags = "g") {let regex = expression instanceof RegExp ? expression : new RegExp(expression, flags)let matches = input.matchAll(regex)matches = [...matches]return matches.map(item => {return {match: item[0],matchAtIndex: item.index,capturedGroups: item.length > 1 ? item.slice(1) : undefined}})}
let input = "key1:value1, key2:value2 "let regex = /(\w+):(\w+)/g
let matches = regexMatch(input, regex)
console.log(matches)

单线解决方案:

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

所以你可以这样使用(必须使用 /g):

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

结果:

[" format_abc"]

只需使用RegExp。1美元…$n个组例如:

1.匹配第一组RegExp。1美元

  1. 匹配第二组RegExp。2美元

如果你在regex likey中使用3组(注意在string.match(regex)之后使用)

RegExp.1美元RegExp.2美元RegExp.3美元

 var str = "The rain in ${india} stays safe";var res = str.match(/\${(.*?)\}/ig);//i used only one group in above example so RegExp.$1console.log(RegExp.$1)

//easiest way is use RegExp.$1 1st group in regex and 2nd grounp like//RegExp.$2 if exist use after match
var regex=/\${(.*?)\}/ig;var str = "The rain in ${SPAIN} stays ${mainly} in the plain";var res = str.match(regex);for (const match of res) {var res = match.match(regex);console.log(match);console.log(RegExp.$1) 
}

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

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

我以为你只是想抓住所有包含abc子字符串和商店匹配组/条目的单词,所以我做了这个脚本:

s = 'something format_abc another word abc abc_somestring'console.log(s.match(/\b\w*abc\w*\b/igm));

  • \b-单词边界
  • \w*-0+字字符
  • abc-完全匹配
  • \w*-0+字字符
  • \b-单词边界

引用:正则表达式:匹配包含某个单词的所有单词https://javascript.info/regexp-introduction