8

我希望能够做一个正则表达式,我可以在其中识别某种类型的字母[dgos],例如,并在我的正则表达式中使用它......但是每当使用来自该字母的字母时,它就会将其从银行中取出。

所以可以说,\1它能够以某种方式代表那组字母 ( dgos)。我可以写一个像这样的正则表达式:

^\1{1}o\1{2}$

它基本上会匹配:
\1{1}= [dgos]{1}
o
\1{2}=[dgos]{2}减去第一个中使用的任何内容

匹配项可以包括good, dosg, sogd, 等...并且不包括sods(因为s必须使用两次)或sooo(因为o必须使用两次)。

我还希望能够识别可以多次使用的字母。我开始自己写这个,但后来意识到我什至不知道从哪里开始,所以我也四处搜索,没有找到一种非常优雅的方式来做到这一点,或者一种灵活的方式来做到这一点足以用最少的输入轻松生成正则表达式。

我在下面有一个使用条件和多个正则表达式组合的解决方案(请随意评论对该答案的想法 - 也许这是我必须这样做的方式?),但如果可能的话,我更喜欢单个正则表达式解决方案......如果可能的话,还有更优雅和高效的东西。

请注意,更高级别的优雅和单个正则表达式部分只是我的喜好,最重要的是它可以工作并且性能足够好。

4

5 回答 5

1

After thinking about it some more, I have thought of a way that this IS possible, although this solution is pretty ugly! For example, suppose you want to match the letters "goods" (i.e. including TWO "o"s):

 ^(?=.*g)(?=.*o.*o)(?=.*d)(?=.*s).{5}$

- So, I have used forward lookaheads to check that all of these letters are in the text somewhere, then simply checked that there are exactly 5 letters.

Or as another example, suppose we want to match the letters "banana", with a letter "a" in position 2 of the word. Then you could match:

 ^(?=.*b)(?=.*a.*a.*a)(?=.*n.*n).a.{4}$
于 2013-07-23T08:02:57.080 回答
1

在@TomLord 的回答的基础上,考虑到您不一定需要用尽大量信件,您可以使用否定的前瞻断言而不是肯定的断言。对于您的示例D<2 from bank>R<0-5 from bank>,该正则表达式将是

/^(?![^o]*o[^o]*o)(?![^a]*a[^a]*a)(?![^e]*e[^e]*e[^e]*e)(?![^s]*s[^s]*s)d[oaes]{2}r[oaes]{0,5}$/i

解释:

^                        # Start of string
(?![^o]*o[^o]*o)         # Assert no more than one o
(?![^a]*a[^a]*a)         # Assert no more than one a
(?![^e]*e[^e]*e[^e]*e)   # Assert no more than two e
(?![^s]*s[^s]*s)         # Assert no more than one s
d                        # Match d
[oaes]{2}                # Match two of the letters in the bank
r                        # Match r
[oaes]{0,5}              # Match 0-5 of the letters in the bank
$                        # End of string

您也可以写(?!.*o.*o)而不是(?![^o]*o[^o]*o),但后者更快,只是更难阅读。另一种写法是(?!(?:[^o]*o){2})(如果重复次数增加,则很有用)。

当然,您需要考虑字符串“固定”部分中的字母数量(在这种情况下(dr)不会干扰银行,但在其他示例中它们可能会这样做)。

于 2013-07-23T13:19:33.627 回答
1

假设您使用的是 javascript 版本 1.5+(因此您可以使用前瞻),这是我的解决方案:

^([dogs])(?!.*\1)([dogs])(?!.*\2)([dogs])(?!.*\3)[dogs]$

因此,在每个字母匹配后,您执行一个否定的前瞻,以确保这个匹配的字母不再出现。

但是,如果您想允许重复某些字母,则此方法不起作用(或者至少需要变得复杂得多!)。(例如,如果您要匹配的字母是“示例”。)

编辑:我只是重新思考了一下,这是一个更优雅的解决方案:

^(?:([dogs])(?!.*\1)){4}
于 2013-07-22T17:01:21.267 回答
0

这样的东西很容易指定,只是很长

gdos|gdso|gods|gosd|....

您基本上会指定每个排列。只需编写一些代码来生成每个排列,结合备用运算符,你就完成了!

虽然......实际上对用于生成排列的决策树进行编码可能是值得的......

我发誓我想我之前在 stackoverflow 上回答过这个问题。让我回你...

于 2013-07-22T17:15:37.317 回答
0

我想不出在正则表达式中完全做到这一点的方法,但我想出了这个:http: //jsfiddle.net/T2TMd/2/

由于 jsfiddle 仅限于帖子大小,因此我无法在那里制作更大的字典。在此处查看使用 180k 单词词典的更好示例。

主功能:

/*
* filter = regex to filter/split potential matches
* bank = available letters
* groups = capture groups that use the bank
* dict = list of words to search through
*/
var matchFind = function(filter, bank, groups, dict) {
    var matches = [];
    for(var i=0; i < dict.length; i++) {
        if(dict[i].match(filter)){
            var fail = false;
            var b = bank;
            var arr = dict[i].split(filter);
            //loop groups that use letter bank
            for(var k=0; k<groups.length && !fail; k++) {
                var grp = arr[groups[k]] || [];
                //loop characters of that group
                for(var j=0; j<grp.length && !fail; j++) {
                    var regex = new RegExp(b);
                    var currChar = grp.charAt(j);
                    if(currChar.match(regex)) {
                        //match found, remove from bank
                        b = b.replace(currChar,"");
                    } else { 
                        fail = true;
                    }
                }
            }
            if(!fail) {
                matches.push(dict[i]);
            }
        }
    }
    return matches;
}

用法:

$("#go").click( function() {
    var f = new RegExp($("#filter").val());
    var b = "["+$("#bank").val().replace(/[^A-Za-z]+/g,"").toUpperCase()+"]";
    var g = $("#groups").val().replace(/[^0-9,]+/g,"").split(",") || [];
    $("#result").text(matchFind(f,b,g,dict).toString());
});

为了更容易创建场景,我也创建了这个:

$("#build").click( function() {
    var bank = "["+$("#buildBank").val().replace(/[^A-Za-z]+/g,"").toUpperCase()+"]";
    var buildArr = $("#builder").val().split(",");
    var groups = [];
    var build = "^";
    for(var i=0; i<buildArr.length; i++) {
        var part = buildArr[i];
        if(/\</.test(part)) {
            part = "(" + bank + part.replace("<", "{").replace(">", "}").replace("-",",") + ")";
            build = build + part;
            groups.push(i+1);
        } else {
            build = build + "("+part+")";
        }
    }
    build = build + "$";
    $("#filter").val(build);
    $("#bank").val(bank);
    $("#groups").val(groups.toString());
    $("#go").click();
});

这在拼字游戏中很有用,所以假设你处于一个单词必须以“D”开头的位置,在“D”和平行单词的“R”之间有两个空格,你OAEES有你的信银行。我可以在构建器中放入D,<2>,R,<0-3>,因为它必须以 D 开头,然后必须有 2 个来自银行的字母,然后有一个 R,然后我最多可以使用 3 个字母(因为我在两者之间使用了 2 个字母) D 和 R)。

构建器将使用字母库并转换D,<2>,R,<0-3>^(D)([OAEES]{2})(R)([OAEES]{0,5})$用于过滤可能匹配的字母库。然后通过这些可能的匹配,它将逐个字符地查看使用字母库的捕获组,当它找到它们时从该正则表达式中删除字母,这样如果使用的字母库字母多于那里,它将不匹配是在信银行。

在这里测试上述场景。

于 2013-07-22T15:44:26.803 回答