以任何顺序匹配可选捕获组

在解析用户输入时存在许多情况,其中用户有机会向输入添加若干可选标志,这些标志应该以任何顺序被接受。 如何使用正则表达式对其进行解析,以便每个标志位于其自己的捕获组中(如果存在)?

例如:

有一个必需的令牌a ,然后是3个可选的令牌,它们可以按任何顺序排列bcd

一些可接受的输入将是:

 a ab ac abc acb abcd adbc acdb 

捕获组应始终如下所示:

 0 => (anything, this is ignored) 1 => a 2 => b or null 3 => c or null 4 => d or null 

这个问题有几个部分已经得到解答:

  1. 使用(...)? 表单以使捕获组可选
  2. 使用先行(?=.*b)(?=.*c)(?=.*d)允许事物处于任何顺序

但这些策略的组合不起作用: (a)(?=.*(b)?)(?=.*(c)?)(?=.*(d)?)

Regex101测试

正则表达式允许以任何顺序找到可选令牌?

(答案可以使用任何风格的正则表达式)

适用于多种口味的正则表达式是:

 (a)(?=(?:.*(b))?)(?=(?:.*(c))?)(?=(?:.*(d))?) 

这种forms是模块化的,因为添加它只需要在模式上添加另一个(?=(?:.*(xxx))?) 。 它起作用是因为它迫使.*进行回溯,但也保持一个.*? 立即退出(因为下一个令牌可以立即匹配)。

Regex101经过测试 (适用于PCRE,JavaScript和Python)

JavaScript示例: JSFiddle

 var cmd = document.getElementById("cmd"), pre = document.getElementById("output"), reg = /(a)(?=(?:.*(b))?)(?=(?:.*(c))?)(?=(?:.*(d))?)/; cmd.onkeyup = function() { var m = reg.exec(cmd.value) || [], output = "Match\n"; for (var i = 1; i < m.length; i++) output += "[" + i + "] => " + (m[i] || "null") + "\n"; pre.innerHTML = m.length ? output : "No Match"; } 
 Enter command:  
No Match