ChatGPT解决这个技术问题 Extra ChatGPT

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

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

var myString = "某事 format_abc"; // 我想要 "abc" var arr = /(?:^|\s)format_(.*?)(?:\s|$)/.exec(myString);控制台.log(arr); // 打印:[" format_abc", "abc"] .. 到目前为止一切顺利。控制台.log(arr[1]); // 打印:undefined (???) console.log(arr[0]); // 打印:format_undefined (!!!)

我究竟做错了什么?

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

"date format_%A"

报告“%A”未定义似乎是一种非常奇怪的行为,但它与这个问题没有直接关系,所以我开了一个新问题,Why is a matched substring returning "undefined" in JavaScript?

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


C
Cymen

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

var myString = "某事 format_abc"; var myRegexp = /(?:^|\s)format_(.*?)(?:\s|$)/g; var myRegexp = new RegExp("(?:^|\s)format_(.*?)(?:\s|$)", "g"); var match = myRegexp.exec(myString);控制台.log(匹配[1]); // abc

如果有多个匹配项,您可以遍历它们:

var myString = "某事 format_abc"; var myRegexp = new RegExp("(?:^|\s)format_(.*?)(?:\s|$)", "g");匹配 = myRegexp.exec(myString); while (match != null) { // 匹配文本: match[0] // 匹配开始: match.index // 捕获组 n: match[n] console.log(match[0]) match = myRegexp.exec(我的字符串); }

编辑:2019-09-10

如您所见,迭代多个匹配项的方式不是很直观。这导致了 String.prototype.matchAll 方法的提议。这种新方法预计将在 ECMAScript 2020 specification 中发布。它为我们提供了一个干净的 API 并解决了多个问题。它已开始登陆主流浏览器和 JS 引擎,如 Chrome 73+ / Node 12+ 和 Firefox 67+。

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

const string = "something format_abc"; const 正则表达式 = /(?:^|\s)format_(.*?)(?:\s|$)/g;常量匹配 = string.matchAll(regexp); for (const match of matches) { console.log(match); console.log(match.index) }

因为它返回一个迭代器,我们可以说它是惰性的,这在处理特别大量的捕获组或非常大的字符串时很有用。但如果您需要,可以使用 spread syntaxArray.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]);
}

同时,虽然此提议得到更广泛的支持,但您可以使用 official shim package

此外,该方法的内部工作很简单。使用生成器函数的等效实现如下:

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 属性的突变而产生副作用。

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

我也很高兴看到 discussions of the proposal 中甚至引用了这个 StackOverflow 问题。


+1 请注意,在第二个示例中,您应该使用 RegExp 对象(不仅是“/myregexp/”),因为它保留了对象中的 lastIndex 值。不使用 Regexp 对象,它将无限迭代
@ianaz:我不相信这是真的? http://jsfiddle.net/weEg9/ 似乎至少可以在 Chrome 上运行。
为什么上面的而不是:var match = myString.match(myRegexp); // alert(match[1])
不需要显式的“新 RegExp”,但是除非指定 /g,否则将发生无限循环
需要注意的是,第 0 个索引是整个匹配项。所以 const [_, group1, group2] = myRegex.exec(myStr); 是我的模式。
B
Blowsie

您可以使用以下方法获取每场比赛的第 n 个捕获组:

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


这是一个远远优于其他答案的答案,因为它正确地显示了所有匹配项的迭代,而不是只得到一个。
M
Michael

var myString = "某事 format_abc"; var arr = myString.match(/\bformat_(.*?)\b/);控制台.log(arr[0] + " " + arr[1]);

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


正好相反。 '\b' 分隔单词。 word= '\w' = [a-zA-Z0-9_] 。 “format_a_b”是一个词。
@BF老实说,我在 6 年前添加了“不适用于 format_a_b”作为事后的想法,我不记得我在那里的意思...... :-) 我想它的意思是“无法捕捉仅a”,即。 format_ 之后的第一个字母部分。
我想说 \b(--format_foo/}\b 不返回 "--format_foo/" 因为 "-" 和 "/" 不是 \word 字符。但是 \b(format_a_b)\b 确实返回 "format_a_b “。对吧?我指的是你在圆括号中的文字声明。(没有反对投票!)
请注意,g 标志在这里很重要。如果将 g 标志添加到模式中,您将获得一个匹配数组,而忽略捕获组。 "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]
S
Sebastien H.

最后但同样重要的是,我发现了一行代码对我来说很好用(JS ES6):

让 reg = /#([\S]+)/igm; // 获取主题标签。 let string = 'mi alegría es total! ✌🙌\n#fiestasdefindeaño #PadreHijo #buenosmomentos #france #paris';让匹配 = (string.match(reg) || []).map(e => e.replace(reg, '$1')); console.log(匹配);

这将返回:

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

A
Alexz

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

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


W
Wiktor Stribiżew

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

使用 matchAll,您可以避免 while 循环并使用 /g... 执行。相反,通过使用 matchAll,您可以返回一个迭代器,您可以使用更方便的 for...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 匹配 = myString.matchAll(/(\w+)[:=-](\w+)/g); console.log([...matches]); // 全部匹配捕获组值

console.log([...matches]) 显示

https://i.stack.imgur.com/hfFbK.png

您还可以使用获取匹配值或特定组值

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", "key3=value3" ] console.log(Array.from(matches, m => m[1]) ); // 所有匹配(第 1 组)值 // => [ "key1", "key2", "key3" ]

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


键值对的完美示例。简洁易读,使用非常简单。此外,更好的错误处理,spread 将返回一个空数组而不是 null,因此不再有“错误,没有 null 的属性“长度””
D
Daniel Hallgren

此答案中使用的术语:

Match 表示针对字符串运行 RegEx 模式的结果,如下所示:someString.match(regexPattern)。

匹配模式表示输入字符串的所有匹配部分,它们都位于匹配数组中。这些都是输入字符串中的模式实例。

匹配组表示要捕获的所有组,在 RegEx 模式中定义。 (括号内的模式,如下所示:/format_(.*?)/g,其中 (.*?) 将是匹配组。)它们位于匹配模式中。

描述

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

代码示例

下面是函数 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 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

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


J
Jonathan Lonowski

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

A
Andre Carneiro

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


如果没有全局标志,这将返回所有匹配项,有了它,你只会得到一个大的,所以要小心。
D
David Cheung

使用 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”,查询字符串:“一些=参数“}


N
Nabil Kadimi

仅当您有一对括号时才实用的单行:

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

为什么不while (match = myRegex.exec(myStr)) matches.push(match[1])
e
eyelidlessness

使用您的代码:

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

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


N
Nisse Engström

函数getMatches(字符串,正则表达式,索引){索引|| (索引 = 1); // 默认为第一个捕获组 var matches = [];变量匹配; while (match = regex.exec(string)) {matches.push(match[index]); } 返回匹配项; } // 示例:var myString = 'Rs.200 在 02-12-14 20:05:49 借记到 A/c ...2031(清除 Bal Rs.66248.77)AT ATM。免费电话 1800223344 18001024455 (6am-10pm)'; var myRegEx = /clear bal.+?(\d+\.?\d{2})/gi; // 获取包含每个匹配项的第一个捕获组的数组 var matches = getMatches(myString, myRegEx, 1); // 记录结果 document.write(matches.length + ' 找到匹配项:' + JSON.stringify(matches)) console.log(matches);

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


C
Community

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

/\bformat_(.*?)\b/

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


这是一个以空格分隔的列表,所以我认为 \s 会很好。奇怪的是该代码对我不起作用(FF3 Vista)
是的,确实很奇怪。您是否在 Firebug 控制台中单独尝试过它?我的意思是从一个空白的页面开始。
M
MSS

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

让 text = `File1 File2 `;让 fileUrls = [...text.matchAll(/href="(http\:\/\/[^"]+\.\w{3})\"/g)].map(r => r[ 1]); console.log(fileUrls);


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

c
ccpizza

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

var str = "我们的主要武器是 {1}、{0} 和 {2}!"; var params= ['惊喜', '恐惧', '无情效率']; var patt = /{([^}]+)}/g; str=str.replace(patt, function(m0, m1, position){return params[parseInt(m1)];}); document.write(str);

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


M
Md. A. Barik

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

/([a-z])\1/

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


C
Caio Santos

一线解决方案:

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

所以你可以这样使用(必须使用/g):

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

结果:

[" format_abc"]

ß
ßãlãjî

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

1.匹配第一组RegExp.$1

匹配第二组 RegExp.$2

如果您在 regex likey 中使用 3 组(注意在 string.match(regex) 之后使用)

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

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

//最简单的方法是在正则表达式中使用 RegExp.$1 第一组和第二组,例如 //RegExp.$2 如果存在匹配后使用 var regex=/\${(.*?)\}/ig; var str = "${SPAIN} 的雨在 ${mainly} 中停留在平原上"; var res = str.match(正则表达式); for (const match of res) { var res = match.match(regex);控制台.log(匹配); console.log(RegExp.$1) }


K
Kamil Kiełczewski

获取所有组出现

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


D
Delcon

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

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

然后从下面剪断函数

/** * @param {字符串 | number} 输入 * 要匹配的输入字符串 * @param {regex | string} 表达式 * 正则表达式 * @param {string} 标志 * 可选标志 * * @returns {array} * [{ match: '...', matchAtIndex: 0, captureGroups: [ '...', '.. .' ] }] */ function regexMatch(input, expression, flags = "g") { let regex = expression instanceof RegExp ?表达式 : new RegExp(expression, flags) let matches = input.matchAll(regex) matches = [...matches] return matches.map(item => { return { match: item[0], matchAtIndex: item.index, captureGroups: item.length > 1 ? item.slice(1) : undefined } }) } let input = "key1:value1, key2:value2" let regex = /(\w+):(\w+)/g let matches =正则表达式匹配(输入,正则表达式)console.log(匹配)


T
Tomerikoo

我以为您只想获取包含 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