JavaScript正则表达式中如何访问匹配组
技术背景
在JavaScript里,正则表达式是处理字符串的强大工具,而访问正则表达式中的匹配组是一项常见需求。以往访问多个匹配结果的方式不够直观,因此提出了 String.prototype.matchAll
方法,该方法已被纳入ECMAScript 2020规范。
实现步骤
旧方法
在 String.prototype.matchAll
方法出现之前,我们可以使用 RegExp.prototype.exec
方法来访问匹配组:
1 2 3 4 5 6 7 8 9 10 11 12
| var myString = "something format_abc"; var myRegexp = new RegExp("(?:^|\\s)format_(.*?)(?:\\s|$)", "g"); var matches = myRegexp.exec(myString); console.log(matches[1]);
var match; while (match = myRegexp.exec(myString)) { console.log(match[0]); console.log(match.index); console.log(match[1]); }
|
新方法 String.prototype.matchAll
String.prototype.matchAll
方法返回一个迭代器,使用起来更加方便:
1 2 3 4 5 6 7 8
| const string = "something format_abc"; const regexp = /(?:^|\s)format_(.*?)(?:\s|$)/g; const matches = string.matchAll(regexp);
for (const match of matches) { console.log(match); console.log(match.index); }
|
若需要将结果转换为数组,可以使用扩展运算符或 Array.from
方法:
1 2 3 4 5 6 7 8 9
| function getFirstGroup(regexp, str) { const array = [...str.matchAll(regexp)]; return array.map(m => m[1]); }
function getFirstGroup(regexp, str) { return Array.from(str.matchAll(regexp), m => m[1]); }
|
核心代码
自定义 matchAll
方法
1 2 3 4 5 6 7 8
| 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; } }
|
查找所有匹配模式及包含的匹配组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const searchString = (string, pattern) => string .match(new RegExp(pattern.source, pattern.flags)) .map(match => new RegExp(pattern.source, pattern.flags) .exec(match));
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; };
|
最佳实践
使用命名组
在ES2018中,可以使用命名组让正则表达式更具可读性:
1 2 3 4
| 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);
|
使用 String.prototype.replace
处理多个匹配结果
1 2 3 4 5 6 7 8 9
| 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);
|
常见问题
无限循环问题
使用 RegExp.prototype.exec
方法时,如果正则表达式没有全局标志 g
,可能会导致无限循环。因此在使用时要确保正则表达式带有全局标志。
浏览器兼容性问题
String.prototype.matchAll
方法并非所有浏览器都支持,在使用时需要查看 浏览器兼容性 信息。若需要在不支持的浏览器中使用,可以使用 官方垫片包。