在解析用户输入的许多情况下,用户有机会向输入添加几个可选标志,这些标志应该以任何顺序接受。如何使用正则表达式对其进行解析,以便每个标志都位于其自己的捕获组中(如果存在)?
例如:
有一个必需的 token a
,然后是 3 个可选 token ,可以按任意顺序出现 b
、c
和 d
.
一些可接受的输入是:
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
这个问题的几个部分已经得到解答:
- 使用
(...)?
表单将捕获组设置为可选 - 使用前瞻
(?=.*b)(?=.*c)(?=.*d)
允许事物按任意顺序排列
但是这些策略的组合不起作用:(a)(?=.*(b)?)(?=.*(c)?)(?=.*(d)?)
什么正则表达式允许以任何顺序找到可选标记?
(答案可以使用任何风格的正则表达式)
最佳答案
适用于多种风格的正则表达式是:
(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)?
太懒了(它停在第一个索引处,因为它注意到下一项是可选的)。
关于regex - 以任意顺序匹配可选捕获组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37449492/