在 JavaScript 正则表达式中命名捕获组?

IT技术 javascript regex
2021-02-05 17:12:56

据我所知,JavaScript 中没有命名捕获组这样的东西。获得类似功能的替代方法是什么?

6个回答

ECMAScript 2018 将命名捕获组引入 JavaScript 正则表达式。

例子:

  const auth = 'Bearer AUTHORIZATION_TOKEN'
  const { groups: { token } } = /Bearer (?<token>[^ $]*)/.exec(auth)
  console.log(token) // "Prints AUTHORIZATION_TOKEN"

如果您需要支持较旧的浏览器,您可以使用普通(编号)捕获组执行命名捕获组可以执行的所有操作,您只需要跟踪数字 - 如果捕获组的顺序在您的正则表达式更改。

我能想到的命名捕获组只有两个“结构”优势:

  1. 在某些正则表达式风格(.NET 和 JGSoft,据我所知)中,您可以对正则表达式中的不同组使用相同的名称(有关此问题的示例,请参见此处)。但无论如何,大多数正则表达式都不支持此功能。

  2. 如果您需要在被数字包围的情况下引用编号的捕获组,则可能会出现问题。假设您要向数字添加零,因此要替换(\d)$10在 JavaScript 中,这会起作用(只要您的正则表达式中的捕获组少于 10 个),但 Perl 会认为您正在寻找反向引用 number10而不是 number 1,后跟0. 在 Perl 中,您可以${1}0在这种情况下使用

除此之外,命名的捕获组只是“语法糖”。仅在您真正需要它们时才使用捕获组,并(?:...)在所有其他情况下使用非捕获组这会有所帮助

JavaScript 的更大问题(在我看来)是它不支持冗长的正则表达式,这会使创建可读的、复杂的正则表达式变得容易得多。

Steve Levithan 的 XRegExp 库解决了这些问题。

所谓的语法糖 确实有助于提高代码的可读性!
2021-03-13 17:12:56
许多风格允许在正则表达式中多次使用相同的捕获组名称。但是只有 .NET 和 Perl 5.10+ 通过保留参与匹配的名称的最后一组捕获的值使这特别有用。
2021-03-18 17:12:56
截至 2019 年 10 月,Firefox、IE 11 和 Microsoft Edge(Chromium 之前)不支持命名组捕获。大多数其他浏览器(甚至 Opera 和三星手机)都可以。caniuse.com/…
2021-03-20 17:12:56
巨大的优势是:你可以只改变你的 RegExp,没有数字到变量的映射。非捕获组解决了这个问题,除了一种情况:如果组的顺序发生变化怎么办?此外,将这个额外的字符放在其他组上很烦人......
2021-04-05 17:12:56
我认为命名捕获组还有另一个非常有value的原因。例如,如果您想使用正则表达式从字符串中解析日期,您可以编写一个灵活的函数来获取值和正则表达式。只要正则表达式为年、月和日期命名了捕获,您就可以用最少的代码运行一系列正则表达式。
2021-04-10 17:12:56

另一种可能的解决方案:创建一个包含组名和索引的对象。

var regex = new RegExp("(.*) (.*)");
var regexGroups = { FirstName: 1, LastName: 2 };

然后,使用对象键来引用组:

var m = regex.exec("John Smith");
var f = m[regexGroups.FirstName];

这使用正则表达式的结果提高了代码的可读性/质量,但不是正则表达式本身的可读性。

您可以使用XRegExp,这是一种增强的、可扩展的、跨浏览器的正则表达式实现,包括对附加语法、标志和方法的支持:

  • 添加新的正则表达式和替换文本语法,包括对命名捕获的全面支持
  • 添加两个新的正则表达式标志:s, 使点匹配所有字符(又名 dotall 或单行模式),和x, 用于自由间距和注释(又名扩展模式)。
  • 提供一套函数和方法,使复杂的正则表达式处理变得轻而易举。
  • 自动修复正则表达式行为和语法中最常见的跨浏览器不一致问题。
  • 让您轻松创建和使用插件,为 XRegExp 的正则表达式语言添加新的语法和标志。

在 ES6 中,您可以使用数组解构来捕获您的组:

let text = '27 months';
let regex = /(\d+)\s*(days?|months?|years?)/;
let [, count, unit] = regex.exec(text) || [];

// count === '27'
// unit === 'months'

注意:

  • 最后一个逗号let跳过结果数组的第一个值,这是整个匹配的字符串
  • 当没有匹配项时|| []after.exec()将防止解构错误(因为.exec()将返回null
第一个逗号是因为 match 返回的数组的第一个元素是输入表达式,对吗?
2021-03-15 17:12:56
String.prototype.match返回一个数组:位置 0 处的整个匹配字符串,然后是其后的任何组。第一个逗号表示“跳过位置 0 处的元素”
2021-04-07 17:12:56
对于那些具有转译或 ES6+ 目标的人,我最喜欢的答案是这里。如果重用的正则表达式更改,这不一定会防止不一致错误以及命名索引,但我认为这里的简洁很容易弥补这一点。在字符串可能是或 的地方选择了RegExp.prototype.execover String.prototype.matchnullundefined
2021-04-09 17:12:56

更新:它终于变成了 JavaScript (ECMAScript 2018)!


命名的捕获组很快就会进入 JavaScript。
它的提案已经处于第 3 阶段。

(?<name>...)对于任何标识符名称,可以使用语法在尖括号内为捕获组指定一个名称。日期的正则表达式可以写成/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u. 每个名称都应该是唯一的,并遵循 ECMAScript IdentifierName的语法

命名组可以从正则表达式结果的组属性的属性中访问。与未命名的组一样,还会创建对组的编号引用。例如:

let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
let result = re.exec('2015-01-02');
// result.groups.year === '2015';
// result.groups.month === '01';
// result.groups.day === '02';

// result[0] === '2015-01-02';
// result[1] === '2015';
// result[2] === '01';
// result[3] === '02';
这是目前的第 4 阶段提案。
2021-03-22 17:12:56
也可以使用空值合并(以防命名捕获组工作): let {year, month, day} = {...re.exec('2015-01-02')?.groups};
2021-04-02 17:12:56
如果您使用的是 '18,不妨全力以赴进行解构; let {year, month, day} = ((result) => ((result) ? result.groups : {}))(re.exec('2015-01-02'));
2021-04-07 17:12:56