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

nic*_*ckf 1277 javascript regex

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

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 (!!!)
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?


我发现有什么不对上述正则表达式代码:实际的字符串,我反对是这样的测试:

"date format_%A"
Run Code Online (Sandbox Code Playgroud)

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


问题是console.log它的参数就像一个printf语句,因为我正在记录的字符串("%A")有一个特殊的值,它试图找到下一个参数的值.

CMS*_*CMS 1585

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

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

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

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);
}
Run Code Online (Sandbox Code Playgroud)

  • +1请注意,在第二个示例中,您应该使用RegExp对象(不仅仅是"/ myregexp /"),因为它将lastIndex值保留在对象中.不使用Regexp对象,它将无限迭代 (107认同)
  • 不需要显式的"新RegExp",但是除非指定了/ g,否则将发生无限循环 (28认同)
  • 为什么以上代替:`var match = myString.match(myRegexp); // alert(匹配[1])`? (14认同)
  • @ianaz:我不相信这是真的吗?[http://jsfiddle.net/weEg9/](http://jsfiddle.net/weEg9/)似乎至少可以在Chrome上运行. (6认同)
  • 另一种不进入无限循环的方法是明确地更新字符串,例如`string = string.substring(match.index + match [0] .length)` (3认同)
  • 顶部代码片段在代码片段运行器中产生“未捕获的类型错误:无法读取 null 的属性(读取“1”)”。 (2认同)

Mat*_*ens 179

这是一种方法,您可以使用它来获得每个匹配的第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);
Run Code Online (Sandbox Code Playgroud)

  • mnn是对的.如果'g'标志不存在,这将产生无限循环.这个功能要非常小心. (13认同)
  • 这是对其他人的一个非常优越的答案,因为它正确显示了所有匹配的迭代,而不是只获得一个. (12认同)
  • @MichaelMikowski现在你刚刚隐藏了无限循环,但你的代码运行缓慢.我认为最好让代码以不好的方式中断,以便在开发中捕获它.将一些bs最大迭代放入其中是很草率的.隐藏问题而不是解决根本原因不是答案. (5认同)
  • 我对此进行了改进,使其类似于python的re.findall().它将所有匹配组合成一个数组数组.它还修复了全局修饰符无限循环问题.http://jsfiddle.net/ravishi/MbwpV/ (4认同)
  • @MichaelMikowski,当你没有达到执行限制时,没有那么慢.当你的时候,它显然要慢得多.我不是说你的代码不起作用,我说在实践中我认为它会带来更多弊大于利.在开发环境中工作的人将看到代码在无负载下正常工作,尽管执行了大量代码的10,000次不必要的执行.然后他们会把它推到生产环境中,并想知道为什么他们的应用程序在负载下降.根据我的经验,如果事情以明显的方式破坏,并且在开发周期的早期阶段就会更好. (4认同)
  • @wallacer确保达到执行限制时代码变慢,但这就是为什么在开发过程中发生这种情况时会发出警告(甚至是异常)的原因。但是,当您投入生产时,代码将更加持久。这类似于使用setInterval与setTimout。我的建议是永远不要使用setInterval,因为它本质上很危险。而是使用setTimeout并在需要时让例程本身进行调用。 (2认同)
  • @MichaelMikowski只是在闲逛我的旧评论,看到我在这里从未回应。当然,在最坏的情况下,使用最大迭代中断来防止循环,并且以明显的方式使其失败总比没有好。不过,首先,要看一下实际算法并弄清楚为什么它没有完成,以及是否可以将其更改为始终完成。无限循环是算法设计不佳的征兆,并且在几乎所有情况下,比“如果尝试太多次则中断”更好的解决方法。这是一个懒惰的hack修复。有地方吗,但通常不是推荐的解决方案 (2认同)

Phi*_*Lho 56

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

\b不完全是一回事.(它起作用--format_foo/,但不起作用format_a_b)但我想展示你的表达的替代品,这很好.当然,match通话很重要.

  • 恰恰相反。'\ b'分隔单词。字='\ w'= [a-zA-Z0-9_]。“ format_a_b”是一个词。 (2认同)

小智 30

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

var matches = mystring.match(/(?:neededToMatchButNotWantedInResult)(matchWanted)/igm);
Run Code Online (Sandbox Code Playgroud)

在看了上面带有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); } );
Run Code Online (Sandbox Code Playgroud)

在此之后,我认为我不会再使用.match()了.


Seb*_* H. 23

最后但并非最不重要的是,我发现一行代码对我来说很好(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);
Run Code Online (Sandbox Code Playgroud)

这将返回:

['fiestasdefindeaño', 'PadreHijo', 'buenosmomentos', 'france', 'paris']
Run Code Online (Sandbox Code Playgroud)


Dan*_*ren 18

本答案中使用的术语:

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

描述

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

代码示例

下面是一个函数的一个例子searchString,它返回一个Array所有的匹配的模式,其中每一个match是一个Array与所有的含有匹配组.我没有使用while循环,而是提供了使用Array.prototype.map()函数和更for高效的方法的示例- 使用plain -loop.

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

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

// 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
Run Code Online (Sandbox Code Playgroud)

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

// 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
Run Code Online (Sandbox Code Playgroud)

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


Jon*_*ski 17

你的语法可能不是最好的.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"]
Run Code Online (Sandbox Code Playgroud)


And*_*iro 16

无需调用该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...
Run Code Online (Sandbox Code Playgroud)

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

  • 如果没有全局标志,则返回所有匹配项,只有它才能获得一个大标记,所以请注意这一点. (3认同)
  • 这有效并且感觉更自然。 (2认同)

Wik*_*żew 16

String#matchAll (see the Stage 3 Draft / December 7, 2018 proposal), simplifies acccess to all groups in the match object (mind that Group 0 is the whole match, while further groups correspond to the capturing groups in the pattern):

With matchAll available, you can avoid the while loop and exec with /g... Instead, by using matchAll, you get back an iterator which you can use with the more convenient for...of, array spread, or Array.from() constructs

This method yields a similar output to Regex.Matches in C#, re.finditer in Python, preg_match_all in PHP.

See a JS demo (tested in Google Chrome 73.0.3683.67 (official build), beta (64-bit)):

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

The console.log([...matches]) shows

在此处输入图片说明

You may also get match value or specific group values using

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" ]
Run Code Online (Sandbox Code Playgroud)

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


Nab*_*imi 8

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

while ( ( match = myRegex.exec( myStr ) ) && matches.push( match[1] ) ) {};
Run Code Online (Sandbox Code Playgroud)

  • 为什么不`while(match = myRegex.exec(myStr))matches.push(match [1])` (4认同)

eye*_*ess 7

使用你的代码:

console.log(arr[1]);  // prints: abc
console.log(arr[0]);  // prints:  format_abc
Run Code Online (Sandbox Code Playgroud)

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


小智 6

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);
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)


Dav*_*ung 6

使用es2018,您现在可以String.match()使用命名组,使您的正则表达式更加明确地说明其尝试执行的操作。

const url =
  '/sf/ask/30274541/?some=parameter';
const regex = /(?<protocol>https?):\/\/(?<hostname>[\w-\.]*)\/(?<pathname>[\w-\./]+)\??(?<querystring>.*?)?$/;
const { groups: segments } = url.match(regex);
console.log(segments);
Run Code Online (Sandbox Code Playgroud)

你会得到像

{协议:“ https”,主机名:“ stackoverflow.com”,路径名:“ questions / 432493 / how-do-you-access-the-matched-groups-in-javascript-regular-expression”,查询字符串:“ some = parameter“}


PEZ*_*PEZ 5

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

/\bformat_(.*?)\b/
Run Code Online (Sandbox Code Playgroud)

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