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

时间:2009-01-11 07:21:20

标签: javascript regex

我想使用regular expression匹配字符串的一部分,然后访问带括号的子字符串:

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”未定义似乎是一种非常奇怪的行为,但它与此问题没有直接关系,所以我打开了一个新的 Why is a matched substring returning "undefined" in JavaScript? 。< / p>


问题是console.log将其参数视为printf语句,并且由于我正在记录的字符串("%A")具有特殊值,因此它试图找到该值下一个参数。

23 个答案:

答案 0 :(得分:1494)

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

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

答案 1 :(得分:169)

以下是您可以使用的方法来获取每个匹配的 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);

答案 2 :(得分:52)

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

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

答案 3 :(得分:28)

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

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

答案 4 :(得分:20)

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

&#13;
&#13;
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);
&#13;
&#13;
&#13;

这将返回:

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

答案 5 :(得分:16)

您的语法可能不是最好的。 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"]

答案 6 :(得分:13)

本回答中使用的术语:

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

描述

要访问匹配的组,在每个匹配的模式中,您需要一个函数或类似的东西来迭代匹配。正如许多其他答案所示,有很多方法可以做到这一点。大多数其他答案使用while循环迭代所有匹配的模式,但我想我们都知道这种方法的潜在危险。有必要匹配new RegExp()而不仅仅是模式本身,这只是在评论中提到的。这是因为.exec()方法的行为类似于生成器函数 - it stops every time there is a match,但保持其.lastIndex在下一个.exec()处从那里继续调用

代码示例

下面是一个函数searchString的示例,该函数返回所有匹配模式的Array ,其中每个match都是Array,所有包含匹配的组。我没有使用while循环,而是使用Array.prototype.map()函数以及更高效的方式提供示例 - 使用普通for - 循环。

简明版本(代码少,语法糖多)

由于它们基本上实现了forEach - 循环而不是更快的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版本(更多代码,更少语法糖)

// 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 :(得分:11)

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

答案 8 :(得分:7)

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

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

答案 9 :(得分:6)

使用您的代码:

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

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

答案 10 :(得分:6)

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

  

matchAll可用的情况下,您可以避免while循环,而在exec中避免/g。相反,通过使用matchAll,您可以迭代器,您可以将其与更方便的for...ofarray spreadArray.from()构造一起使用

此方法产生的输出类似于C#中的Regex.Matches,Python中的re.finditer和PHP中的preg_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" ]

注意:请参见browser compatibility详细信息。

答案 11 :(得分:5)

&#13;
&#13;
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 = '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 match
var matches = getMatches(myString, myRegEx, 1);

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

&#13;
&#13;
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);
&#13;
&#13;
&#13;

答案 12 :(得分:2)

借助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”,路径名:“ questions / 432493 / how-do-you-access-the-matched-groups-in-a-javascript-regular-expression”, querystring:“ some = parameter”}

    
  

答案 13 :(得分:2)

即使我同意正则表达式应该是PhiLo,你的代码也适合我(Mac上的FF3):

/\bformat_(.*?)\b/

(但是,当然,我不确定,因为我不知道正则表达式的背景。)

答案 14 :(得分:1)

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

答案 15 :(得分:1)

您实际上并不需要显式循环来解析多个匹配项-如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);

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

答案 16 :(得分:1)

单行解决方案:

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

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

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

结果:

[" format_abc"]

答案 17 :(得分:1)

仅使用RegExp。$ 1 ... $ n组 例如:

1。匹配第一组RegExp。$ 1

  1. 匹配第二组RegExp。$ 2

如果您在正则表达式中使用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.$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)
 
}

答案 18 :(得分:0)

  

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

/([a-z])\1/

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

答案 19 :(得分:0)

获取所有组的出现情况

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

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

console.log(m);

答案 20 :(得分:0)

我就像我一样,希望正则表达式返回这样的对象:

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

答案 21 :(得分:0)

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

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

答案 22 :(得分:0)

我以为您只想获取包含 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 - 一个词边界

参考文献:Regex: Match all the words that contains some word https://javascript.info/regexp-introduction