正则表达式可选的非捕获组

17

我是一个完全不懂正则表达式的新手,花了几个小时尝试解决这个谜题。 我认为我必须使用某种可选的非捕获组或选择。

我想匹配以下字符串:

  1. Neuer Film a von 1000

  2. Neuer Film a von 1000 mit b

  3. Neuer Film a von 1000 mit b und c

  4. Neuer Film a von 1000 mit b und c und d

  5. Neuer Film a mit b

  6. Neuer Film a mit b und c

  7. Neuer Film a mit b und c und d

我的正则表达式看起来像这样:

var regex = /(?:[nN]euer [Ff]ilm\s?)(.*)(?:[vV]on).(\d{4}).(?:[Mm]it)(.*)(?:[uU]nd)(.*)/g;
问题是它仅匹配字符串3和4。它不匹配最后两个"und",但将其打包在第3组而不是第4组中。 有人可以帮忙修复我的正则表达式吗(它并不非常用户友好;)
1个回答

19

您真的需要使用非捕获可选组(例如(?:...)?),但此外,您还需要锚点^匹配字符串的开头和$匹配字符串末尾)和懒惰的点匹配模式.*?,以尽可能少地匹配任何字符)。

您可以使用

/^[nN]euer [Ff]ilm\s*(.*?)(?:\s*[vV]on\s+(\d{4}))?(?:\s+[Mm]it\s*(.*?)(?:\s*[uU]nd\s*(.*))?)?$/
请参见正则表达式演示。在演示中,/gm修饰符是必需的,因为输入是多行字符串。

模式详情:

  • ^ - 字符串开头锚点
  • [nN]euer [Ff]ilm - Neuer film / Neuer Film / neuer Film
  • \s* - 零个或多个空白字符
  • (.*?) - 第1组: 除换行符之外的任意0个或更多个字符(尽可能少地匹配,即到下一个子模式左侧最靠近的位置)
  • (?:\s*[vV]on\s+(\d{4}))? - 匹配1个或0个非捕获组:
    • \s* - 0个或多个空白字符
    • [vV]on - vonVon
    • \s+ - 1个或多个空白字符
    • (\d{4}) - 第2组:4个数字
  • (?:\s+[Mm]it\s*(.*?)(?:\s*[uU]nd\s*(.*))?)? - 匹配1个或0个非捕获组,包含:
    • \s+ - 1个或多个空白字符
    • [Mm]it - Mitmit
    • \s* - 0个或多个空白字符
    • (.*?) - 第3组:除换行符之外的任意0个或更多个字符(尽可能少地匹配)
    • (?:\s*[uU]nd\s*(.*))? - 匹配1个或0个非捕获组,包含:
      • \s*[uU]nd\s* - 包含0个或多个空白字符的undUnd
      • (.*) - 第4组:除换行符之外的任意0个或更多个字符(尽可能多地匹配)
  • $ - 字符串结尾。
var strs = ['Neuer Film a von 1000','Neuer Film a von 1000 mit b','Neuer Film a von 1000 mit b und c','Neuer Film a von 1000 mit b und c und d','Neuer Film a mit b','Neuer Film a mit b und c','Neuer Film a mit b und c und d'];
var rx = /^[nN]euer [Ff]ilm\s*(.*?)(?:\s*[vV]on\s+(\d{4}))?(?:\s+[Mm]it\s*(.*?)(?:\s*[uU]nd\s*(.*))?)?$/;
for (var s of strs) {
   var m = rx.exec(s);
   if (m) {
     console.log('-- ' + s + ' ---');
     console.log('Group 1: ' + m[1]);
     if (m[2]) console.log('Group 2: ' + m[2]);
     if (m[3]) console.log('Group 3: ' + m[3]);
     if (m[4]) console.log('Group 4: ' + m[4]);
   }
   
}


1
非捕获组仍然会“消耗”文本,它们不像回顾断言或前瞻断言那样是零宽度的。 - Wiktor Stribiżew
非常感谢您的回答。我从未想过要使用像您描述的非捕获组可选项,因为我仍然希望它们出现在匹配组中(认为(...)?就足够了)。我也从未想过这种懒惰点匹配。但我仍然不明白为什么第一个“und”没有被捕获,而后面的“und”却被捕获了,尽管它们在一个非捕获组中? - TrantSteel
2
.*? 会尽可能地匹配少的字符,因此正则表达式会到达 und 的最左边出现位置(用 \s*[uU]nd\s* 进行匹配而不被捕获)。请注意,懒惰模式并不匹配两个字符串之间的 最短 子字符串(正如某些 SO 回答所述),它们只匹配随后子模式的最左边出现位置。 - Wiktor Stribiżew
1
我添加了一些细节并缩短了正则表达式,因为一些非捕获组(既不被量化也不包含交替)是多余的。 - Wiktor Stribiżew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接