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


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

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.log 将其参数视为a printf 声明,因为我正在记录的字符串("%A")有一个特殊的值,它试图找到下一个参数的值。


1027
2018-01-11 07:21


起源




答案:


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

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

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

var myString = "something format_abc";
var myRegexp = /(?:^|\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);
}


1310
2018-01-11 07:26



+1请注意,在第二个示例中,您应该使用RegExp对象(不仅是“/ myregexp /”),因为它将lastIndex值保留在对象中。如果不使用Regexp对象,它将无限迭代 - ianaz
@ianaz:我不相信这是真的吗? http://jsfiddle.net/weEg9/ 似乎至少可以在Chrome上运行。 - spinningarrow
为什么以上而不是: var match = myString.match(myRegexp); // alert(match[1])? - JohnAllen
不需要显式的“新RegExp”,但是除非指定了/ g,否则将发生无限循环 - George Chen
另一种不进入无限循环的方法是明确地更新字符串,例如, string = string.substring(match.index + match[0].length) - Olga


这是一个可以用来获得的方法 ñ为每场比赛捕获组:

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


153
2018-01-08 08:26



这是对其他人的一个非常优越的答案,因为它正确地显示了所有匹配的迭代,而不是仅获得一个。 - Rob Evans
mnn是对的。如果'g'标志不存在,这将产生无限循环。这个功能要非常小心。 - Druska
我对此进行了改进,使其类似于python的re.findall()。它将所有匹配组合成一个数组数组。它还修复了全局修饰符无限循环问题。 jsfiddle.net/ravishi/MbwpV - ravishi
@MichaelMikowski现在你已经隐藏了无限循环,但你的代码运行缓慢。我认为最好让代码以一种糟糕的方式中断,以便在开发过程中捕获它。将一些bs最大迭代放入其中是很草率的。隐藏问题而不是解决根本原因不是答案。 - wallacer
@MichaelMikowski,当你没有达到执行限制时,没有那么慢。当你的时候,它显然要慢得多。我不是说你的代码不起作用,我说在实践中我认为它会带来更多弊大于利。在开发环境中工作的人将看到代码在无负载下正常工作,尽管执行了大量代码的10,000次不必要的执行。然后他们会把它推到生产环境中,并想知道为什么他们的应用程序在负载下降。根据我的经验,如果事情以明显的方式破坏,并且在开发周期的早期阶段就会更好。 - wallacer


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

\b 不是完全一样的事情。 (它适用于 --format_foo/,但不起作用 format_a_b)但我想展示你表达的另一种选择,这很好。当然, match 通话是重要的事情。


48
2018-01-11 09:10



这完全相反。 '\ b'分隔单词。 word ='\ w'= [a-zA-Z0-9_]。 “format_a_b”是一个单词。 - B.F.
@ B.F.Honestly,我补充说“不起作用 format_a_b“作为6年前的思想,我不记得我的意思了...... :-)我想这意味着”无法捕捉 a 只有“,即之后的第一个字母部分 format_。 - PhiLho
我想说\ b( - format_foo /} \ b不返回“--format_foo /”,因为“ - ”和“/”都没有\ word字符。但是\ b(format_a_b)\ b会返回“format_a_b” “对吧?我在圆括号中引用你的文字说明。(没有投票!) - B.F.


关于上面的多匹配括号示例,我在找不到我想要的内容之后在这里寻找答案:

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()了。


19
2017-07-17 04:53





你的语法可能不是最好的。 FF / Gecko将RegExp定义为Function的扩展。
(FF2竟然如此 typeof(/pattern/) == 'function'

这似乎是特定于FF - IE,Opera和Chrome都为它抛出异常。

相反,使用其他人先前提到的方法: RegExp#exec 要么 String#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"]

15
2018-01-11 12:55





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

var reg = /#([\S]+)/igm; //get hashtags
var string = 'mi alegría es total! \n#fiestasdefindeaño #PadreHijo #buenosmomentos #france #paris';

var matches = (string.match(reg) || []).map(e => e.replace(reg, '$1'));
console.log(matches);

这将返回: [fiestasdefindeaño, PadreHijo, buenosmomentos, france, paris]


9
2018-01-03 14:40



这是有效的,除非匹配找不到任何东西,然后地图不起作用。 - MBehtemam


本答案中使用的术语:

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

描述

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

代码示例

以下是功能的示例 searchString 返回一个 Array 所有 匹配的模式,每个 match 是一个 Array 包含所有内容 匹配组。我没有使用while循环,而是使用了两个 Array.prototype.map() 功能以及更高效的方式 - 使用普通的 for-循环。

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

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

// 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

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


7
2017-08-23 22:36





只有在您有一对括号时才能实用的单线程:

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

5
2017-07-12 15:41



为什么不 while (match = myRegex.exec(myStr)) matches.push(match[1]) - willlma
@willlma是的!.. - Nabil Kadimi


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


5
2018-06-19 19:47



这种工作和感觉更自然。 - Vidar


使用你的代码:

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

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


4
2018-01-11 07:27