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

var myString=“something format_abc”;//我要“abc”var arr=/(?:^|\s)format_(.*?)(?:\s|$)/.exec(myString);控制台日志(arr);//打印:[“format_abc”,“abc”]。。到现在为止,一直都还不错。控制台日志(arr[1]);//打印:未定义(???)控制台日志(arr[0]);//打印:format_undefined(!!!)

我做错了什么?


我发现上面的正则表达式代码没有任何问题:我测试的实际字符串是这样的:

"date format_%A"

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


问题是console.log像printf语句一样接受其参数,因为我记录的字符串(“%a”)有一个特殊值,所以它试图查找下一个参数的值。


更新:2019-09-10

旧的迭代多个匹配的方法不是很直观。这导致了String.prototype.matchAll方法的提出。这种新方法在ECMAScript 2020规范中。它为我们提供了一个干净的API并解决了多个问题。自从Chrome 73+/Node 12+和Firefox 67+之后,它就出现在主流浏览器和JS引擎中。

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

const string=“something format_abc”;常量regexp=/(?:^|\s)format_(.*?)(?:\s|$)/g;const matches=string.matchAll(正则表达式);for(匹配的常量匹配){console.log(匹配);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属性的突变而产生的副作用。

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

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

原始答案

您可以这样访问捕获组:

var myString=“something format_abc”;var myRegexp=/(?:^|\s)format_(.*?)(?:\s|$)/g;var myRegexp=新RegExp(“(?:^|\s)format_(.*?)(?:\s|$)”,“g”);var matches=myRegexp.exec(myString);console.log(匹配[1]);//abc

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

var myString=“something format_abc”;var myRegexp=新RegExp(“(?:^|\s)format_(.*?)(?:\s|$)”,“g”);match=myRegexp.exec(myString);while(匹配!=null){//匹配的文本:匹配[0]//匹配开始:match.index//捕获组n:匹配[n]console.log(匹配[0])match=myRegexp.exec(myString);}


使用代码:

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

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


var myString=“something format_abc”;var arr=myString.match(/\bformat_(.*?)\b/);控制台日志(arr[0]+“”+arr[1]);

\b并不完全相同。(它适用于--format_foo/,但不适用于format_a_b)但我想展示一个替代表达式,这很好。当然,比赛电话是最重要的。


即使我同意PhiLo的正则表达式应该是:

/\bformat_(.*?)\b/

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


您的语法可能不是最好保留的。FF/Gekko将RegExp定义为Function的扩展。(FF2达到typeof(/patter/)==“函数”)

这似乎是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"]

这里有一个方法可以用来获得n​每个匹配的第个捕获组:

函数getMatches(字符串、正则表达式、索引){索引||(索引=1);//默认为第一个捕获组var matches=[];var匹配;while(match=regex.exec(字符串)){matches.push(match[index]);}返回匹配;}//示例:var myString=“something format_abc something format_def something form_ghi”;var myRegEx=/(?:^|\s)format_(.*?)(?:\s|$)/g;//获取包含每个匹配的第一个捕获组的数组var matches=getMatches(myString,myRegEx,1);//日志结果document.write(matches.length+'找到匹配项:'+JSON.stringify(匹配项))console.log(匹配项);


只有当你有一对括号时才实用的一行:

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

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

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()做任何事情了。


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

函数getMatches(字符串、正则表达式、索引){索引||(索引=1);//默认为第一个捕获组var matches=[];var匹配;while(match=regex.exec(字符串)){matches.push(match[index]);}返回匹配;}//示例:var myString='200卢比记入账户。。。2031年2月12日20:05:49(结算余额66248.77卢比)ATM。免费电话180022334418001024455(上午6点至晚上10点);var myRegEx=/清除bal.+?(\d+\.?\d{2})/gi;//获取包含每个匹配的第一个捕获组的数组var matches=getMatches(myString,myRegEx,1);//日志结果document.write(matches.length+'找到匹配项:'+JSON.stringify(匹配项))console.log(匹配项);

函数getMatches(字符串、正则表达式、索引){索引||(索引=1);//默认为第一个捕获组var matches=[];var匹配;while(match=regex.exec(字符串)){matches.push(match[index]);}返回匹配;}//示例:var myString=“something format_abc something format_def something form_ghi”;var myRegEx=/(?:^|\s)format_(.*?)(?:\s|$)/g;//获取包含每个匹配的第一个捕获组的数组var matches=getMatches(myString,myRegEx,1);//日志结果document.write(matches.length+'找到匹配项:'+JSON.stringify(匹配项))console.log(匹配项);


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


本回答中使用的术语:

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

描述

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

代码示例

下面是一个函数searchString的示例,它返回所有匹配模式的数组,其中每个匹配都是一个包含所有匹配组的数组。我没有使用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

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


使用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”,主机名:“stackerflow.com”,路径名:“questions/432493/how-do-you-acess-the-matched-groups-in-a-javascript-regular-expression”,querystring:“some=parameter”}


您实际上不需要一个显式循环来解析多个匹配项-传递一个替换函数作为第二个参数,如String.protype.replace(regex,func)中所述:

var str=“我们的主要武器是{1}、{0}和{2}!”;var params=[“惊讶”、“恐惧”、“无情的效率”];var模式=/{([^}]+)}/g;str=str.replace(patt,函数(m0,m1,位置){return params[parseInt(m1)];});document.write(str);

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


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

如果matchAll可用,则可以避免while循环并使用/g执行。。。相反,通过使用matchAll,您可以返回一个迭代器,您可以更方便地使用它。。。of、array spread或array.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]);//所有匹配捕获组值

console.log([…matches])显示

您还可以使用

let matchData=“key1:value1,key2-value2!!@key3=value3”.matchAll(/(\w+)[:=-](\w+)/g)var matches=[…matchData];//注意matchAll结果不可重复console.log(Array.from(matches,m=>m[0]));//所有匹配(组0)值//=>[“key1:value1”,“key2-value2”,“key 3=value3”]console.log(Array.from(matches,m=>m[1]));//所有匹配(组1)值//=>[“key1”,“key2”,“key 3”]

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


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

/([a-z])\1/

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


获取所有组事件

设m=[],s=“something format_abc format_def format_ghi”;s.replace(/(?:^|\s)format_(.*?)(?:\s|$)/g,(x,y)=>m.push(y));控制台日志(m);


如果你和我一样,希望正则表达式返回这样的Object:

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

然后从下面剪切函数

/***@param{string | number}输入*要匹配的输入字符串*@param{regex | string}表达式*正则表达式*@param{string}标志*可选标志* *@return{array}* [{匹配:“…”,matchAtIndex:0,捕获的组:[“…”,“…”]}] */函数regexMatch(输入,表达式,标志=“g”){让regex=RegExp的表达式实例?表达式:新RegExp(表达式,标志)let matches=input.matchAll(正则表达式)匹配=[…匹配]return matches.map(项=>{返回{匹配:项[0],matchAtIndex:项索引,捕获组:item.length>1?item.s切片(1):未定义}})}let input=“key1:value1,key2:value2”让正则表达式=/(\w+):(\w+)/glet matches=regexMatch(输入,regex)console.log(匹配项)


单线解决方案:

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.

匹配第二组RegExp$2.

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

注册Exp$1 RegExp$2 RegExp$3.

var str=“${印度}的雨很安全”;var res=str.match(/\${(.*?)\}/ig);//我在上面的例子中只使用了一个组,所以RegExp$1.console.log(RegExp.$1)

//最简单的方法是使用RegExp$1正则表达式中的第一组和第二组//注册Exp$如果存在,则在匹配后使用2var正则表达式=/\${(.*?)\}/ig;var str=“${SPAIN}的雨水主要停留在平原”;var res=str.match(正则表达式);for(res的常量匹配){var res=match.match(正则表达式);console.log(匹配);console.log(RegExp.$1)}


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

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


我以为你只想抓取包含abc子字符串的所有单词并存储匹配的组/条目,所以我编写了这个脚本:

s='some format_abc另一个单词abc abc_somestring'console.log(s.match(/\b\w*abc\w*\b/igm));

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

参考:Regex:匹配包含某个单词的所有单词https://javascript.info/regexp-introduction