按任意顺序匹配可选捕获组

时间:2016-05-26 00:01:44

标签: javascript java regex language-agnostic pcre

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

例如:

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

一些可接受的输入是:

a
a b
a c
a b c
a c b
a b c d
a d b c
a c d b

捕获组应始终如下所示:

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

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

  1. 使用(...)?表单使捕获组可选
  2. 使用前瞻(?=.*b)(?=.*c)(?=.*d)允许事物处于任何顺序
  3. 但这些策略的组合不起作用:(a)(?=.*(b)?)(?=.*(c)?)(?=.*(d)?)

    Regex101 Test

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

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

1 个答案:

答案 0 :(得分:1)

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

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

此表单是模块化的,因为添加它只需要在模式上添加另一个(?=(?:.*(xxx))?)。它起作用是因为它强制.*进行回溯,但也会立即停止.*?(因为下一个标记可以立即匹配)。

Regex101 Tested (在此处使用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: <input id="cmd" type="text" />
<pre id="output">No Match</pre>

问题中两个策略的组合不起作用,因为.*(x)?形式过于贪婪(它跳过捕获组)。另一方面,.*?(x)?太懒了(它在第一个索引处停止,因为它注意到下一个项是可选的)。