代码之家  ›  专栏  ›  技术社区  ›  Jonathan Leffler

什么PerlRegex可以匹配数字12345的连续子集?

  •  6
  • Jonathan Leffler  · 技术社区  · 14 年前

    我想要一个PerlRegex,它匹配字符串“12345”的任何连续子集。

    我可能只是大脑冻结,但这是我的测试代码和目前最好的regex。我可以看到如何通过添加备选方案来强行改变这种情况,但我想知道我缺少哪种优雅的备选方案。[ 我不需要为数字特别捕获;我已经将示例regex留下了没有捕获括号的部分,以使它稍微不那么混乱。 ]

    测试用例:

    use strict;
    use warnings;
    
    my @good = qw( 1 12 123 1234 12345 2 23 234 2345 3 34 345 4 45 5);
    my @bad  = qw( 0 6 13 134 1345 145 15 124 1245 125 1235 24 245 25
                   35 21 32 43 54 543 5432 54321);
    
    my $qr = qr/^(1?(2?(3(4(5)?)?)?)?)$/;   # 3 'good', 3 'bad' failures
    #my $qr = qr/^(1?(2(3(4(5)?)?)?)?)$/;   # 6 'good' failures.
    my $fail = 0;
    
    foreach my $opt (@good)
    {
        printf "GOOD %d: $opt - missed by regex\n", ++$fail if ($opt !~ /$qr/);
    }
    
    foreach my $opt (@bad)
    {
        printf "BAD %d: $opt - allowed by regex\n", ++$fail if ($opt =~ /$qr/);
    }
    
    print(($fail == 0) ? "PASS\n" : "FAIL\n");
    

    样品输出:

    案例1(注释掉):

    GOOD 1: 3 - missed by regex
    GOOD 2: 34 - missed by regex
    GOOD 3: 345 - missed by regex
    GOOD 4: 4 - missed by regex
    GOOD 5: 45 - missed by regex
    GOOD 6: 5 - missed by regex
    FAIL
    

    案例2(现行):

    GOOD 1: 4 - missed by regex
    GOOD 2: 45 - missed by regex
    GOOD 3: 5 - missed by regex
    BAD 4: 13 - allowed by regex
    BAD 5: 134 - allowed by regex
    BAD 6: 1345 - allowed by regex
    FAIL
    

    那么,你能写一个很好的、简单的、对称的正则表达式来匹配我想要的而不是我不想要的吗?


    这个regex允许测试用例通过,但并不像我希望的那样优雅:

    my $qr = qr/^((1?(2(3(4(5)?)?)?)?)|(3?(4(5)?)?)|5)$/;
    

    Justin解决方案的测试用例

    use strict;
    use warnings;
    
    my @good = qw( 1 12 123 1234 12345 2 23 234 2345 3 34 345 4 45 5);
    my @bad  = qw( 0 6 13 134 1345 145 15 124 1245 125 1235 24 245 25
                   35 21 32 43 54 543 5432 54321 11 122 1233 1223 12234);
    
    #my $qr = qr/^(1?(2?(3(4(5)?)?)?)?)$/;   # 3 'good', 3 'bad' failures
    #my $qr = qr/^(1?(2(3(4(5)?)?)?)?)$/;    # 6 'good' failures.
    #my $qr = qr/^((1?(2(3(4(5)?)?)?)?)|(3?(4(5)?)?)|5)$/;  # Passes
    
    # Ysth's solution - passes
    #my $qr = qr/^[12345](?:(?<=1)2|(?<=2)3|(?<=3)4|(?<=4)5)*$/;
    
    my $fail = 0;
    
    foreach my $opt (@good)
    {
        printf "GOOD %d: $opt - missed by regex\n", ++$fail if ('12345' !~ /$opt/);
        #printf "GOOD %d: $opt - missed by regex\n", ++$fail if ($opt !~ /$qr/);
    }
    
    foreach my $opt (@bad)
    {
        printf "BAD %d: $opt - allowed by regex\n", ++$fail if ('12345' =~ /$opt/);
        #printf "BAD %d: $opt - allowed by regex\n", ++$fail if ($opt =~ /$qr/);
    }
    
    print(($fail == 0) ? "PASS\n" : "FAIL\n");
    
    4 回复  |  直到 14 年前
        1
  •  12
  •   Justin    14 年前

    反转匹配:

    '12345' =~ /$opt/
    
        2
  •  7
  •   cjm    14 年前

    以下是贾斯汀想法的修订版:

    index('12345', $opt) >= 0;
    

    或者,如果需要排除空字符串

    index('12345', $opt) >= 0 and length $opt;
    

    这样,你就不需要检查了 $opt 用于regex元字符。我不确定哪个版本会更快。

        3
  •  1
  •   ysth    14 年前
    /^[12345](?:(?<=1)2|(?<=2)3|(?<=3)4|(?<=4)5)*\z/
    

    对不起,两次都弄错了。这应该可以做到。不过,所有可能匹配的显式列表将更快。

        4
  •  0
  •   Gabe Timothy Khouri    14 年前

    你想要比这更好的东西吗

    /\b(1|12|123|1234|12345|2|23|234|2345|3|34|345|4|45|5)\b/

    ?