匹配xa?b?c的正则表达式?但不是单独的x

时间:2010-11-12 20:12:32

标签: regex

我正在尝试编写一个匹配xa?b?c的正则表达式?但不是x。实际上,'x','a','b'和'c'不是单个字符,它们是中等复杂的子表达式,所以我试图避免像x这样的东西(abc | ab | ac | bc | A | b | C)。是否有一种简单的方法可以在正则表达式中匹配“a,b和c中的至少一个,按顺序”,或者我运气不好?

7 个答案:

答案 0 :(得分:11)

这是最短的版本:

(a)?(b)?(c)?(?(1)|(?(2)|(?(3)|(*FAIL))))

如果你需要在一个单独的组中保持匹配,请写下:

((a)?(b)?(c)?)(?(2)|(?(3)|(?(4)|(*FAIL))))

但在abc包含捕获组的情况下,这不是很强大。所以写下这个:

(?<A>a)?(?<B>b)?(?<C>c)?(?(<A>)|(?(<B>)|(?(<C>)|(*FAIL))))

如果你需要一个小组参加整个比赛,那就写下来:

(?<M>(?<A>a)?(?<B>b)?(?<C>c)?(?(<A>)|(?(<B>)|(?(<C>)|(*FAIL)))))

如果像我一样你更喜欢多字母标识符,并且认为这种事情在/x模式下是疯狂的,请写下:

(?x)
(?<Whole_Match>
    (?<Group_A> a) ?
    (?<Group_B> b) ?  
    (?<Group_C> c) ?

    (?(<Group_A>)           # Succeed 
      | (?(<Group_B>)       # Succeed
          | (?(<Group_C>)   # Succeed
              |             (*FAIL)
            )
        )
    )
 )

这是完整的测试程序,以证明所有工作:

#!/usr/bin/perl
use 5.010_000;

my @pats = (
    qr/(a)?(b)?(c)?(?(1)|(?(2)|(?(3)|(*FAIL))))/,
    qr/((a)?(b)?(c)?)(?(2)|(?(3)|(?(4)|(*FAIL))))/,
    qr/(?<A>a)?(?<B>b)?(?<C>c)?(?(<A>)|(?(<B>)|(?(<C>)|(*FAIL))))/,
    qr/(?<M>(?<A>a)?(?<B>b)?(?<C>c)?(?(<A>)|(?(<B>)|(?(<C>)|(*FAIL)))))/,
    qr{
        (?<Whole_Match>

            (?<Group_A> a) ?
            (?<Group_B> b) ?
            (?<Group_C> c) ?

            (?(<Group_A>)               # Succeed
              | (?(<Group_B>)           # Succeed
                  | (?(<Group_C>)       # Succeed
                      |                 (*FAIL)
                    )
                )
            )

        )
    }x,
);

for my $pat (@pats) {
    say "\nTESTING $pat";
    $_ = "i can match bad crabcatchers from 34 bc and call a cab";
    while (/$pat/g) {
        say "$`<$&>$'";
    }
}

所有五个版本都会生成此输出:

i <c>an match bad crabcatchers from 34 bc and call a cab
i c<a>n match bad crabcatchers from 34 bc and call a cab
i can m<a>tch bad crabcatchers from 34 bc and call a cab
i can mat<c>h bad crabcatchers from 34 bc and call a cab
i can match <b>ad crabcatchers from 34 bc and call a cab
i can match b<a>d crabcatchers from 34 bc and call a cab
i can match bad <c>rabcatchers from 34 bc and call a cab
i can match bad cr<abc>atchers from 34 bc and call a cab
i can match bad crabc<a>tchers from 34 bc and call a cab
i can match bad crabcat<c>hers from 34 bc and call a cab
i can match bad crabcatchers from 34 <bc> and call a cab
i can match bad crabcatchers from 34 bc <a>nd call a cab
i can match bad crabcatchers from 34 bc and <c>all a cab
i can match bad crabcatchers from 34 bc and c<a>ll a cab
i can match bad crabcatchers from 34 bc and call <a> cab
i can match bad crabcatchers from 34 bc and call a <c>ab
i can match bad crabcatchers from 34 bc and call a c<ab>

甜蜜,嗯?

编辑:对于开头部分的x,只需在匹配开始时放置所需的x,然后在第一个可选的捕获组之前a部分,如下所示:

x(a)?(b)?(c)?(?(1)|(?(2)|(?(3)|(*FAIL))))

或者像这样

(?x)                        # enable non-insane mode

(?<Whole_Match>
    x                       # first match some leader string

    # now match a, b, and c, in that order, and each optional
    (?<Group_A> a ) ?
    (?<Group_B> b ) ?  
    (?<Group_C> c ) ?

    # now make sure we got at least one of a, b, or c
    (?(<Group_A>)           # SUCCEED!
      | (?(<Group_B>)       # SUCCEED!
          | (?(<Group_C>)   # SUCCEED!
              |             (*FAIL)
            )
        )
    )
)

测试句是在没有x部分的情况下构建的,所以它不会起作用,但我想我已经证明了我的意思。请注意,所有xabc都可以是任意复杂的模式(是的,甚至是递归的),而不仅仅是单个字母,而且它不是如果他们使用他们自己的编号捕获组,即使是。

如果你想通过前瞻来做这件事,你可以这样做:

(?x)

(?(DEFINE)
       (?<Group_A> a)
       (?<Group_B> b)
       (?<Group_C> c)
)

x

(?= (?&Group_A)
  | (?&Group_B)
  | (?&Group_C)
)

(?&Group_A) ?
(?&Group_B) ?
(?&Group_C) ?

以下是测试程序中添加到@pats数组的内容,以表明此方法也有效:

qr{
    (?(DEFINE)
        (?<Group_A> a)
        (?<Group_B> b)
        (?<Group_C> c)
    )

    (?= (?&Group_A)
      | (?&Group_B)
      | (?&Group_C)
    )

    (?&Group_A) ?
    (?&Group_B) ?
    (?&Group_C) ?
}x

请注意,即使使用先行技术,我仍然可以管理永远不会重复任何abc

我赢了吗? ☺

答案 1 :(得分:5)

如果你没有前瞻,那就不是一件容易的事。

x(ab?c?|bc?|c)

答案 2 :(得分:5)

这个怎么样:

x(?:a())?(?:b())?(?:c())?(\1|\2|\3)

abc之后的空捕获组将始终匹配(空字符串)ab或{{1}按顺序匹配。

c部分仅在前面的组中至少有一个参与比赛时才匹配。因此,如果您只有(\1|\2|\3),则正则表达式会失败。

正则表达式的每个部分都只会被评估一次。

当然,如果xxab是包含捕获组本身的更复杂的子表达式,则必须相应地调整反向引用的数量*

由于这个正则表达式看起来有点奇怪,这里是详细版本:

c

你可能需要用锚点x # Match x (?:a())? # Try to match a. If this succeeds, \1 will contain an empty string. (?:b())? # Same with b and \2. (?:c())? # Same with c and \3. (\1|\2|\3) # Now try to match the content of one of the backreferences. # This works if one of the empty parentheses participated in the match. # If so, the backref contains an empty string which always matches. # Bingo! ^来包围它,除非你不介意它与字符串$等中的xb匹配。

例如,在Python中:

cxba

*或者,如果你的正则表达式味道知道命名捕获组,你可以使用它们,例如

>>> r=re.compile(r"x(?:a())?(?:b())?(?:c())?(\1|\2|\3)$")
>>> for test in ("x", "xa", "xabc", "xba"):
...     m = r.match(test)
...     if m:
...         print("{} --> {}".format(test, m.group(0)))
...     else:
...         print("{} --> no match".format(test))
...
x --> no match
xa --> xa
xabc --> xabc
xba --> no match

在Python / PCRE中。在.NET(以及可能的其他版本)中,拥有多个使用相同名称的捕获组甚至是合法的,从而可以进行另一种简化:

x(?:a(?P<a>))?(?:b(?P<b>))?(?:c(?P<c>))?((?P=a)|(?P=b)|(?P=c))

答案 3 :(得分:3)

这样的东西
x(?=[abc])a?b?c?

答案 4 :(得分:2)

如果您绝对必须不重复a,b或c,那么这是最短,最简单的正则表达式 - ,前提是x表示固定长度的表达式,或者您正在使用的实现支持可变长度的实现。它使用负面的后视,例如,Perl会在可变长度的后视镜上死掉。

基本上,这就是你所说的,改写:

/(x)a?b?c?(?<!x)/;

这就是它所说的:我想匹配xa?b?c?但是当我考虑它时,我不希望最后一个表达式是 x

此外,如果a,b或c的匹配以x结尾,则无效。 (帽子:tchrist)

答案 5 :(得分:1)

这是我能想到的最短时间:

x(ab?c?|bc?|c)

我相信它符合标准,同时尽量减少重复(虽然有一些)。它还避免使用任何预见或其他处理器密集型表达式,这可能比保存正则表达式字符串长度更有价值。

此版本重复c三次。您可以对其进行调整,以便ab是最经常重复的那个,因此您可以选择最短的abc来是要重复三次的人。

答案 6 :(得分:0)

如果您不需要找到最大(贪婪)匹配,您可以删除“按此顺序”,因为如果您匹配x(a|b|c)并忽略任何后续文本,您已经匹配“至少一个a,b和c,按顺序“。换句话说,如果你需要的只是一个真/假答案(它是否匹配),那么x(a|b|c)就足够了。 (另一个假设是:您正在尝试确定输入字符串是否包含匹配,而不是整个字符串是否与正则表达式匹配。即。请参阅@Alan Moore的问题。)

但是,如果要识别最大匹配或匹配整个输入字符串,可以使用前瞻:x(?=(a|b|c))a?b?c?

那里存在一些冗余,但比你试图避免的组合方法少得多。