使用ASCII码计算顺子扑克牌的正则表达式

4
在另一个问题中我学会了如何使用正则表达式计算普通扑克牌的手牌(这里)。
现在,通过好奇心,问题是:我能用ASCII CODE来计算相同的内容吗? 类似于:
正则表达式:[C][C+1][C+2][C+3][C+4],其中C为ASCII CODE(或类似)
匹配:45678, 23456
不匹配:45679或23459(不在顺序中)

2
你为什么要用正则表达式来做这件事? - NullUserException
1
没有什么大的原因,我只是在学习正则表达式。 - Topera
注意:我已经使用正则表达式计算了其他扑克牌手。现在只剩下顺子。 - Topera
谁也不知道答案?也许在任何编程语言中...我猜Poly会赢 +50 :) - Topera
@Topera:顺便问一下,你在使用哪些正则表达式引擎?我完全支持你学习正则表达式的努力,并且很乐意以任何方式帮助你。 - polygenelubricants
@polygenelubricants:我正在使用Java。非常感谢! - Topera
4个回答

5
你的主要问题是手牌没有使用ASCII连续编码,非面牌使用数字,面牌使用非连续、无序字符。
你需要在字符串开头检测以下内容:2345A, 23456, 34567, ..., 6789T, 789TJ, 89TJQ, 9TJQKTJQKA。这些不是连续的ASCII代码,即使它们是连续的,你也会遇到问题,因为A2345TJQKA都是有效的,而在同一字符集中,你不能同时将A视为小于和大于其他字符。
如果必须使用正则表达式,则可以使用以下正则表达式段:
(2345A|23456|34567|45678|56789|6789T|789TJ|89TJQ|9TJQK|TJQKA)

这可能是您能找到的最简单和最易读的内容。

谢谢。我知道我需要连续和有序的字符。 因此,A23456789TJQKA可以是ABCDEFGHIJKLMN。 - Topera
但是你的“A”不能同时占据两个位置,对吗? - dash-tom-bang

4

正如其他回答所指出的那样,没有任何正则表达式可以做到你想要的,但是你说你想要学习正则表达式,因此这里有另一种可能会有帮助的元正则表达式方法。

以下是一个Java代码片段,给定一个字符串,程序会自动生成一个模式,匹配该字符串中长度为5的任何子字符串。

    String seq = "ABCDEFGHIJKLMNOP";
    System.out.printf("^(%s)$",
        seq.replaceAll(
            "(?=(.{5}).).",
            "$1|"
        )
    );

输出结果为(在 ideone.com 上可见):
^(ABCDE|BCDEF|CDEFG|DEFGH|EFGHI|FGHIJ|GHIJK|HIJKL|IJKLM|JKLMN|KLMNO|LMNOP)$

您可以使用此功能方便地生成正则表达式模式以匹配顺子扑克牌手,通过适当初始化seq


它是如何工作的

.元字符匹配“任何”字符(取决于我们所处的模式,换行符可能是一个例外)。

{5}是精确的重复说明符。.{5}精确匹配5个.

(?=…)向先行断言;它断言给定的模式可以匹配,但由于它只是一个断言,它不会真正地(即消耗)从输入字符串中进行匹配。

简单的(…)是一个捕获组。它创建了一个反向引用,您可以在模式中稍后使用,或在替换中使用,或者任何您认为合适的方式。

该模式在此处重复以方便使用:

     match one char
        at a time
           |
(?=(.{5}).).
\_________/
 must be able to see 6 chars ahead
 (capture the first 5)

该模式逐个匹配一个字符.。在匹配该字符之前,我们使用断言(?=…),断言我们可以看到6个字符(.{5})的总数,并将第一个.{5}捕获到组1中(…)。对于每个这样的匹配,我们使用$1|替换,即由组1捕获的任何内容,后跟交替元字符。让我们考虑当我们将其应用于较短的String seq = "ABCDEFG";时会发生什么。 表示我们当前的位置。
=== INPUT ===                                    === OUTPUT ===

 A B C D E F G                                   ABCDE|BCDEFG
↑
We can assert (?=(.{5}).), matching ABCDEF
in the lookahead. ABCDE is captured.
We now match A, and replace with ABCDE|

 A B C D E F G                                   ABCDE|BCDEF|CDEFG
  ↑
We can assert (?=(.{5}).), matching BCDEFG
in the lookahead. BCDEF is captured.
We now match B, and replace with BCDEF|

 A B C D E F G                                   ABCDE|BCDEF|CDEFG
    ↑
Can't assert (?=(.{5}).), skip forward

 A B C D E F G                                   ABCDE|BCDEF|CDEFG
      ↑
Can't assert (?=(.{5}).), skip forward

 A B C D E F G                                   ABCDE|BCDEF|CDEFG
        ↑
Can't assert (?=(.{5}).), skip forward

       :
       :

 A B C D E F G                                   ABCDE|BCDEF|CDEFG
              ↑
Can't assert (?=(.{5}).), and we are at
the end of the string, so we're done.

所以我们得到了ABCDE|BCDEF|CDEFG,它们都是seq长度为5的子字符串。
参考资料:

Poly,非常感谢你的解释。 - Topera

3

类似于regex: [C][C+1][C+2][C+3][C+4],其中C是ASCII CODE(或类似的内容)

在大多数正则表达式语言中,你不能做任何与此相似的事情。这种模式根本不是正则表达式设计的目的。

没有主流的正则表达式模式可以简洁地匹配任意两个连续字符,它们的ASCII编码之间相差为x


用于教学目的...

这是(在ideone.com上查看):

    String alpha = "ABCDEFGHIJKLMN";
    String p = alpha.replaceAll(".(?=(.))", "$0(?=$1|\\$)|") + "$";

    System.out.println(p);
    // A(?=B|$)|B(?=C|$)|C(?=D|$)|D(?=E|$)|E(?=F|$)|F(?=G|$)|G(?=H|$)|
    // H(?=I|$)|I(?=J|$)|J(?=K|$)|K(?=L|$)|L(?=M|$)|M(?=N|$)|N$

    String p5 = String.format("(?:%s){5}", p);

    String[] tests = {
        "ABCDE",    // true
        "JKLMN",    // true
        "AAAAA",    // false
        "ABCDEFGH", // false
        "ABCD",     // false
        "ACEGI",    // false
        "FGHIJ",    // true
    };
    for (String test : tests) {
        System.out.printf("[%s] : %s%n",
            test,
            test.matches(p5)
        );
    }

这里使用元正则表达式技术生成一个模式。该模式确保每个字符后面跟着正确的字符(或字符串结尾),使用前瞻。然后将该模式进行元正则化,以重复匹配5次。
您可以根据需要将alpha替换为您的扑克序列。
请注意,这是一种绝对不切实际的解决方案。更可读的做法是例如仅检查alpha.contains(test) && (test.length() == 5)

相关问题


@Topera:如果正则表达式也能下棋就好了。但是至少正则表达式可以“交换胸部”(Pascal Thivent的话,不是我的)http://stackoverflow.com/questions/3349814/how-do-i-put-a-value-of-an-array-into-another-one/3350191#3350191 - polygenelubricants
@polygenelubricants:2-3-4-5-6是一种模式(ASCII码+1)。下棋不是。 :) - Topera
@polygenelubricants:如果有一天我在正则表达式中找到了这个,你会给我买可乐吗?哈哈哈?(兄弟,我也接受赏金!) - Topera
@Topera:你向我发起了挑战,看看我的最新回答是否能够应对你的挑战。 - polygenelubricants
@polygenelubricants:+1 - 元正则表达式很酷!但仍不是答案。也许它真的不存在.... :( - Topera
显示剩余2条评论

0

问题已解决!

请参见http://jsfiddle.net/g48K9/3

我使用 JavaScript 中的闭包解决了这个问题。

String.prototype.isSequence = function () {
    If (this == "A2345") return true; // an exception
    return this.replace(/(\w)(\w)(\w)(\w)(\w)/, function (a, g1, g2, g3, g4, g5) {
        return    code(g1) == code(g2) -1 &&
                code(g2) == code(g3) -1 &&
                code(g3) == code(g4) -1 &&
                code(g4) == code(g5) -1;
    })
};

function code(card){
    switch(card){
        case "T": return 58;
        case "J": return 59;
        case "Q": return 60;
        case "K": return 61;
        case "A": return 62;
        default: return card.charCodeAt();
    }
}


test("23456");
test("23444");
test("789TJ");
test("TJQKA");
test("8JQKA");

function test(cards) {
    alert("cards " + cards + ": " + cards.isSequence())
}

仅为澄清,ASCII码:

ASCII码:

2 = 50
3 = 51
4 = 52
5 = 53
6 = 54
7 = 55
8 = 56
9 = 57
T = 84 -> 58
J = 74 -> 59
Q = 81 -> 60
K = 75 -> 61
A = 65 -> 62

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