Вот самая короткая версия:
(a)?(b)?(c)?(?(1)|(?(2)|(?(3)|(*FAIL))))
Если вам нужно провести матч в отдельной группе, напишите:
((a)?(b)?(c)?)(?(2)|(?(3)|(?(4)|(*FAIL))))
Но это не очень надежно, если a
, b
или c
содержат группы захвата. Поэтому вместо этого напишите:
(?<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
, поэтому оно не сработает, но я думаю, что я показал, как я собираюсь пойти на это. Обратите внимание, что все x
, a
, b
и c
могут быть произвольно сложными шаблонами (да, даже рекурсивными), а не просто отдельными буквами, и не имеет значения, используют ли они пронумерованные группы захвата их даже.
Если вы хотите пойти на это с предвкушением, вы можете сделать это:
(?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
Вы заметите, пожалуйста, что мне все еще удается никогда не повторять ни одного из a
, b
или c
, даже с техникой прогнозирования.
Я выиграю? ☺