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

How do you access the matched groups in a JavaScript regular expression?

提问人:nickf 提问时间:1/11/2009 最后编辑:Michael M.nickf 更新时间:5/28/2023 访问量:1052252

问:

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

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 中返回“undefined”?


问题在于它像语句一样接受其参数,并且由于我记录的字符串 () 具有特殊值,因此它试图查找下一个参数的值。console.logprintf"%A"

JavaScript 正则表达式

评论

0赞 aderchox 1/25/2023
对于未来的访问者: 请注意,如果使用,则返回的值将是一个“迭代器”(更准确地说是“可迭代迭代器”),但不是“数组”,因此,例如,将起作用,但将是 .另请注意,返回的迭代器是不可重启的。matchAllfor(const match of matches)matches[0]undefined

答:

2001赞 Christian C. Salvadó 1/11/2009 #1

更新日期: 2019-09-10

迭代多个匹配项的旧方法不是很直观。这导致了 String.prototype.matchAll 方法的提出。此新方法位于 ECMAScript 2020 规范中。它为我们提供了一个干净的 API 并解决了多个问题。自 Chrome 73+ / Node 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: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;
  }
}

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

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

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

原始答案

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

var myString = "something format_abc";
var myRegexp = /(?:^|\s)format_(.*?)(?:\s|$)/g;
var myRegexp = new RegExp("(?:^|\\s)format_(.*?)(?:\\s|$)", "g");
var matches = myRegexp.exec(myString);
console.log(matches[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);
}

评论

118赞 ianaz 8/28/2012
+1 请注意,在第二个示例中,您应该使用 RegExp 对象(不仅仅是“/myregexp/”),因为它将 lastIndex 值保留在对象中。如果不使用 Regexp 对象,它将无限迭代
7赞 spinningarrow 10/16/2012
@ianaz:我不相信这是真的吗?至少 http://jsfiddle.net/weEg9/ 似乎可以在 Chrome 上运行。
18赞 JohnAllen 12/31/2013
为什么做上面而不是:?var match = myString.match(myRegexp); // alert(match[1])
30赞 Clucking Turtle 6/7/2014
不需要显式的“new RegExp”,但是除非指定 /g,否则将发生无限循环
2赞 user 7/19/2022
顶部代码段在代码段运行器中生成。Uncaught TypeError: Cannot read properties of null (reading '1')
7赞 eyelidlessness 1/11/2009 #2

使用您的代码:

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

编辑:Safari 3,如果重要的话。

67赞 PhiLho 1/11/2009 #3

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

这并不完全是一回事。(它适用于 ,但不适用于 )但我想展示你的表达方式的替代方案,这很好。当然,电话是重要的事情。\b--format_foo/format_a_bmatch

评论

2赞 B.F. 4/23/2015
这恰恰相反。“\b”分隔单词。word= '\w' = [a-zA-Z0-9_] .“format_a_b”是一个词。
1赞 PhiLho 4/23/2015
@B.F.老实说,我在 6 年前添加了“不起作用”作为事后的想法,我不记得我在那里是什么意思...... :-)我想这意味着“仅捕获不起作用”,即。之后的第一个字母部分。format_a_baformat_
1赞 B.F. 4/23/2015
我想说的是\b(--format_foo/}\b不返回“--format_foo/”,因为“-”和“/”不是\word字符。但是 \b(format_a_b)\b 确实返回“format_a_b”。右?我指的是你用圆括号括起来的发言。(没有投反对票!
1赞 ggorlen 5/16/2021
请注意,标志在这里很重要。如果将标志添加到模式中,您将获得一个匹配数组,而不考虑捕获组。 => 但 => 。gg"a b c d".match(/(\w) (\w)/g);["a b", "c d"]"a b c d".match(/(\w) (\w)/);["a b", "a", "b", index: 0, input: "a b c d", groups: undefined]
4赞 PEZ 1/11/2009 #4

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

/\bformat_(.*?)\b/

(但是,当然,我不确定,因为我不知道正则表达式的上下文。

评论

1赞 nickf 1/11/2009
这是一个空格分隔的列表,所以我认为\s会很好。奇怪的是,该代码对我不起作用(FF3 Vista)
1赞 PEZ 1/11/2009
是的,真的很奇怪。您是否在 Firebug 控制台中单独尝试过?我的意思是从一个原本空白的页面。
18赞 Jonathan Lonowski 1/11/2009 #5

你的语法可能不是最好保留的。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"]
206赞 Mathias Bynens 1/8/2013 #6

下面是可用于获取每个匹配项的第 n个捕获组的方法:

function getMatches(string, regex, index) {
  index || (index = 1); // default to the first capturing group
  var 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 match
var matches = getMatches(myString, myRegEx, 1);

// Log results
document.write(matches.length + ' matches found: ' + JSON.stringify(matches))
console.log(matches);

评论

14赞 Rob Evans 5/11/2013
这是一个比其他答案更好的答案,因为它正确地显示了所有匹配项的迭代,而不是只得到一个匹配项。
9赞 Nabil Kadimi 7/12/2014 #7

仅当有一对括号时才实用的单衬里:

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

评论

4赞 willlma 4/7/2017
为什么不呢while (match = myRegex.exec(myStr)) matches.push(match[1])
36赞 Alexz 7/17/2014 #8

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

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

在查看了上面带有 while 和 .push() 的略微复杂的函数调用后,我突然意识到这个问题可以用 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() 用于几乎任何东西了。

3赞 Pawel Kwiecien 6/28/2015 #9
/*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));
46赞 Sebastien H. 1/3/2017 #10

最后但并非最不重要的一点是,我发现一行代码对我来说很好用(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']
18赞 Andre Carneiro 6/20/2017 #11

无需调用该方法!您可以直接在字符串上使用“match”方法。只是不要忘记括号。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 的第二个匹配项用括号表示。嵌套括号很棘手,所以要小心!

评论

7赞 Shadymilkman01 9/14/2018
如果没有全局标志,它会返回所有匹配项,有了它,你只会得到一个大匹配项,所以要注意这一点。
22赞 Daniel Hallgren 8/24/2017 #12

本答案中使用的术语:

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

描述

若要访问匹配的,在每个匹配的模式中,都需要一个函数或类似的东西来迭代匹配。正如许多其他答案所示,有很多方法可以做到这一点。大多数其他答案都使用 while 循环来遍历所有匹配的模式,但我认为我们都知道这种方法的潜在危险。有必要与模式本身进行匹配,而不仅仅是模式本身,这仅在评论中提到。这是因为该方法的行为类似于生成器函数——每次有匹配项时它都会停止,但在下一次调用时会保持它从那里继续。new RegExp().exec().lastIndex.exec()

代码示例

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

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

它们的性能较低,因为它们基本上实现了 -loop 而不是更快的 -loop。forEachfor

// Concise ES6/ES2015 syntax
const 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 syntax
function 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 syntax
const 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 syntax
function 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

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

10赞 David Cheung 2/12/2019 #13

使用 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);

你会得到类似的东西

{protocol: “https”, hostname: “stackoverflow.com”, pathname: “questions/432493/how-do-you-access-the-matched-groups-in-a-javascript-regular-expression”, querystring: “some=parameter”}

2赞 ccpizza 2/16/2019 #14

你实际上并不需要一个显式循环来解析多个匹配项——将替换函数作为第二个参数传递,如以下所述: String.prototype.replace(regex, func)

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

该参数表示完全匹配的子字符串 , , 等。 表示第一个匹配组,即正则表达式中括在括号中的部分,用于第一个匹配项。and 是找到匹配组的字符串中的起始索引 - 在本例中未使用。m0{0}{1}m10position

27赞 Wiktor Stribiżew 3/8/2019 #15

String#matchAll(参见 Stage 3 Draft / December 7, 2018 提案)简化了对 match 对象中所有组的访问(请注意,Group 0 是整个匹配项,而其他组对应于模式中的捕获组):

有了可用,您可以避免循环,并使用...取而代之的是,通过使用 ,您可以返回一个迭代器,您可以将其与更方便的迭代器一起使用。ofarray spreadArray.from() 构造matchAllwhileexec/gmatchAll

此方法产生的输出与 C#、Python 和 PHP 中的输出类似。Regex.Matchesre.finditerpreg_match_all

请参阅 JS 演示(在 Google Chrome 73.0.3683.67(官方版本)、测试版(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])

enter image description here

您还可以使用

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

注意:请参阅浏览器兼容性详细信息。

评论

0赞 Jarrod McGuire 3/29/2019
键值对的完美示例。简洁易读,使用起来非常简单。此外,更好的错误处理,spread 将返回一个空数组而不是 null,因此不再有“错误,没有 null 的属性”长度”
1赞 Md. A. Barik 7/22/2019 #16

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

/([a-z])\1/

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

1赞 Kamil Kiełczewski 8/21/2019 #17

替换 - 无正则表达式更改

获取所有组出现次数

let m=[], s = "something format_abc  format_def  format_ghi";

s.replace(/(?:^|\s)format_(.*?)(?:\s|$)/g, (x,y)=> m.push(y));

console.log(m);

1赞 Delcon 3/5/2020 #18

我你和我一样,希望正则表达式会返回一个这样的对象:

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

0赞 Caio Santos 4/7/2020 #19

一线解决方案:

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

所以你可以使用这种方式(必须使用 /g):

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

结果:

[" format_abc"]
2赞 Balaji 4/13/2020 #20

只需使用 RegExp.$1...$n 第 组 例如:

1.To 匹配第一组RegExp.$1

  1. 匹配第二组 RegExp.$2

如果你在正则表达式中使用 3 组 likey(注意:在 string.match(regex)) 之后使用

正则表达式 $1 正则表达式 $2 正则表达式 $3

 var str = "The rain in ${india} stays safe"; 
  var res = str.match(/\${(.*?)\}/ig);
  //i used only one group in above example so RegExp.$1
console.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)
 
}

3赞 MSS 8/18/2020 #21

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

评论

0赞 Fusseldieb 8/7/2021
请注意: stackoverflow.com/a/1732454/3525780
0赞 Glorified 5/11/2021 #22

我以为你只想抓取所有包含 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

0赞 crifan 3/17/2023 #23
  • inputStr:"+[NSURL URLWithString:]"
  • JS 正则表达式与组匹配:
    const regexPattern = /^([+-])\[(\w+)\s+([\w:]+)\]$/
    const regexMatch = inputStr.match(regexPattern)
    
  • 输出:regexMatch = ["+[NSURL URLWithString:]", "+", "NSURL", "URLWithString:"]
    • regexMatch[0]= 整个输入str ="+[NSURL URLWithString:]"
    • regexMatch[1]= 组 #1 =([+-]) = "+"
    • regexMatch[2]= 组 #2 =(\w+) = "NSURL"
    • regexMatch[3]= 组 #3 =([\w:]+) = "URLWithString:"