使用捕获组的JavaScript正则表达式中的可选部分

4

我有一个问题,想要实现正则表达式中的可选部分。我拿了一个解析旧文本冒险游戏输入的例子来说明我的任务。这个例子很好地展示了我的需求。以下是一个例子来展示我的目的:

var exp = /^([a-z]+)(?:\s([a-z0-9\s]+)\s(on|with)\s([a-z\s]+))?$/i;

var strings = [
    "look",
    "take key",
    "take the key",
    "put key on table",
    "put the key on the table",
    "open the wooden door with the small rusty key"
];

for (var i=0; i < strings.length;i++) {
    var match = exp.exec(strings[i]);

    if (match) {
        var verb = match[1];
        var directObject = match[2];
        var preposition = match[3];
        var indirectObject = match[4];

        console.log("String: " + strings[i]);
        console.log("  Verb: " + verb);
        console.log("  Direct object: " + directObject);
        console.log("  Preposition: " + preposition);
        console.log("  Indirect object: " + indirectObject);    
    } else {
        console.log("String is not a match: " + strings[i]);
    }
    console.log(match);
}

我的正则表达式对于第一个和最后三个字符串有效。

我知道如何使用其他方法(比如.split())获得正确的结果。这是尝试学习正则表达式,因此我不想寻找另一种替代方法来完成这个任务 :-)

我已经尝试添加更多可选的非捕获组,但是我无法使其工作:

var exp = /^([a-z]+)(?:\s([a-z0-9\s]+)(?:\s(on|with)\s([a-z\s]+))?)?$/i;

这对前三个字符串有效,但对后三个无效。
所以我想要的是: 第一个单词,一些字符直到指定的单词(如 "on"),一些字符直到字符串结尾
棘手的部分是不同的变体。
可以完成吗?
有效解决方案:
exp = /^([a-z]+)(?:\s((?:(?!\s(?:on|with)).)*)(?:\s(on|with)\s(.*))?)?$/i;

1
?: 只是生成一个不匹配的组,与可选项无关。可选项组在末尾有一个 ?,或者被量化为明确的可选项,如 {0,1}。然而,使用太多的可选项组是没有意义的,因为您需要检查每个匹配组是否存在。 - dognose
我知道 ?: 代表一个非捕获组。我尝试使用语法使其变为可选: (?:这部分是可选的)? - Thomas
我认为问题在于第一个可选组定义太贪婪了。它匹配了整个字符串,而不仅仅是直到单词“on”或“with”。 - Thomas
1个回答

2
也许可以使用如下正则表达式进行匹配:
var exp = /^([a-z]+)(?:(?:(?!\s(?:on|with))(\s[a-z0-9]+))+(?:\s(?:on|with)(\s[a-z0-9]+)+)?)?$/i;

\s[a-z0-9]+捕获一个前面有空格的单词。

(?!\s(?:on|with))避免此单词为"on"或"with"。

因此,(?:(?!\s(?:on|with))(\s[a-z0-9]+))+是“on”或“with”之前的单词列表。

您可以在这里测试。


它没有给我完全想要的结果,但是这是朝着正确方向迈出的一步。匹配“小生锈钥匙”的结果变成了“钥匙”,而“木桌”变成了“桌子”。但正如我所说,这是朝着正确方向迈出的一步。我认为“?!-部分”是使其工作的关键。 - Thomas
你想要什么?也许通过移动第一个 ?: 在第一组单词中:^([a-z]+)((?:(?!\s(?:on|with))(?:\s[a-z0-9]+))+(?:\s(?:on|with)(\s[a-z0-9]+)+)?)?$ - Samuel Caillerie
我不确定Stackoverflow上的惯例是什么,但这个答案引导了我正确的方向。我的解决方案是 exp = /^([a-z]+)(?:\s((?:(?!\s(?:on|with)).)*)(?:\s(on|with)\s(.*))?)?$/i; - Thomas
1
好的,实际上你主要是用任何字符组 (.*) 替换了字母数字单词 [a-z0-9]+。如果这样能够正常工作,那就太棒了! - Samuel Caillerie

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