如何在正则表达式中捕获可选组?

How can I capture an optional group in regex?

我有一个正则表达式如下:

    const verseRegex = /(?<chapterBegin>[^\d+$]*):(?<verseBegin>[^\d+$]*)-((?<chapterEnd>[^\d+$]*):)?(?<verseEnd>[^\d+$]*)/g;

我希望正则表达式能够匹配以下两个字符串:

然而,正则表达式只能匹配第一个字符串并将其正确分组:

console.log(verseRegex.exec('4:1-13');

[
  '4:1-13',
  '4',
  '1',
  undefined,
  undefined,
  '13',
  index: 0,
  input: '4:1-13',
  groups: [Object: null prototype] {
    chapterBegin: '4',
    verseBegin: '1',
    chapterEnd: undefined,
    verseEnd: '13'
  }
]

对于第二个字符串 null 返回。 我对上述行为没有任何解释。当我删除可选组并将我的正则表达式重写为:

const verseRegex = /(?<chapterBegin>[^\d+$]*):(?<verseBegin>[^\d+$]*)-(?<chapterEnd>[^\d+$]*):(?<verseEnd>[^\d+$]*)/g;

现在第二个字符串按预期匹配和分组,第一个字符串失败,因为 chapterEnd 组不再是可选的。 我如何重写我的正则表达式以便它匹配和分组两个字符串?

注意 [^\d+$]* 模式匹配除 \d+$ 字符之外的任何字符。你一定是想匹配一个或多个数字块,所以你需要 \d+.

您可以使用

/^(?<chapterBegin>\d+):(?<verseBegin>\d+)-(?:(?<chapterEnd>\d+):)?(?<verseEnd>\d+)$/

或者,没有命名的捕获组(例如,对于 IE):

/^(\d+):(\d+)-(?:(\d+):)?(\d+)$/

regex demo

查看 JavaScript 演示:

const strs = ['4:1-13','4:1-5:20'];
const rx = /^(?<chapterBegin>\d+):(?<verseBegin>\d+)-(?:(?<chapterEnd>\d+):)?(?<verseEnd>\d+)$/;
for (let s of strs) {
  const results = rx.exec(s);
  console.log(s, results.groups);
}

输出:

4:1-13 {
  "chapterBegin": "4",
  "verseBegin": "1",
  "chapterEnd": undefined,
  "verseEnd": "13"
}
4:1-5:20 {
  "chapterBegin": "4",
  "verseBegin": "1",
  "chapterEnd": "5",
  "verseEnd": "20"
}

旧浏览器演示:

var strs = ['4:1-13','4:1-5:20'];
var rx = /^(\d+):(\d+)-(?:(\d+):)?(\d+)$/;
for (var i=0; i<strs.length; i++) {
  var results = rx.exec(strs[i]);
  console.log(strs[i], results);
}