Я пытаюсь написать регулярное выражение, которое соответствует xa? b? c? но не x. В действительности, "x", "a", "b" и "c" не являются одиночными символами, они являются умеренно сложными суб-выражениями, поэтому я пытаюсь избежать чего-то вроде x (abc | ab | ac | bc | | б | в). Есть ли простой способ сопоставить "по крайней мере один из a, b и c в этом порядке" в регулярном выражении, или мне не повезло?
Regex, который соответствует xa? B? C? но не только один
Ответ 1
Имеет самую короткую версию:
(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
могут быть произвольно сложными шаблонами (да, даже рекурсивно), а не только одиночными буквами, и не имеет значения, используют ли они собственные нумерованные группы захвата, даже.
Если вы хотите посмотреть на это с помощью lookaheads, вы можете сделать это:
(?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
даже с помощью метода lookahead.
Я побеждаю? ☺
Ответ 2
Не тривиально, если у вас нет взгляда.
x(ab?c?|bc?|c)
Ответ 3
Как насчет этого:
x(?:a())?(?:b())?(?:c())?(\1|\2|\3)
Пустые группы захвата после a
, b
и c
всегда будут совпадать (пустая строка), если a
, b
или c
совпадают в этом порядке.
Часть (\1|\2|\3)
будет соответствовать только если в матче участвовала хотя бы одна из предыдущих групп. Поэтому, если у вас просто x
, регулярное выражение не выполняется.
Каждая часть регулярного выражения будет оцениваться только один раз.
Конечно, если x
, a
, b
и 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
в строке cxba
и т.д.
Например, в Python:
>>> 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
* или, если ваш аромат регулярного выражения знает имена захваченных групп, вы можете использовать их, например
x(?:a(?P<a>))?(?:b(?P<b>))?(?:c(?P<c>))?((?P=a)|(?P=b)|(?P=c))
в Python/PCRE. В .NET(и, возможно, в других вариантах) даже законно иметь несколько групп захвата, которые используют одно и то же имя, что делает возможным еще одно упрощение:
x(?:a(?<m>))?(?:b(?<m>))?(?:c(?<m>))?\k<m>
Ответ 4
Как насчет чего-то вроде
x(?=[abc])a?b?c?
Ответ 5
Здесь самое короткое, что я мог бы придумать:
x(ab?c?|bc?|c)
Я считаю, что он соответствует критериям, сводя к минимуму повторение (хотя есть некоторые). Это также позволяет избежать использования каких-либо ожиданий или других выражений с интенсивным использованием процессора, что, вероятно, более ценно, чем сохранение длины строки регулярного выражения.
Эта версия повторяется c
три раза. Вы можете адаптировать его так, чтобы чаще всего повторялись a
или b
, поэтому вы могли бы выбрать кратчайший из a
, b
и c
, который будет повторяться три раза.
Ответ 6
Если вам абсолютно не нужно повторять a, b или c, это кратчайшее, простое регулярное выражение - при условии, что x представляет собой выражение с фиксированной длиной или что используемая вами реализация поддерживает переменную длину. Он использует негативный внешний вид, и, например, Perl будет умирать от переменной длины.
В принципе, это то, что вы говорите, перефразировали:
/(x)a?b?c?(?<!x)/;
Вот что он говорит: я хочу совместить xa? b? c? но когда я это считаю, я не хочу, чтобы последнее выражение было х.
Кроме того, он не будет работать, если совпадение для a, b или c заканчивается на x. (шляпка: tchrist)
Ответ 7
Если вам не нужно найти максимальное (жадное) совпадение, вы можете отбросить "в этом порядке", потому что если вы сопоставляете x(a|b|c)
и игнорируете любой следующий текст, который вы уже сопоставили "по крайней мере, один из, b и c в этом порядке". Другими словами, если все, что вам нужно, это истинный/ложный ответ (соответствует ли он или нет), то x(a|b|c)
достаточно. (Другое предположение: вы пытаетесь определить, соответствует ли входная строка совпадению, а не соответствует ли вся строка регулярному выражению. I.e. см. Вопрос @Alan Moore.)
Однако, если вы хотите определить максимальное совпадение или совпадение со всей входной строкой, вы можете использовать lookahead: x(?=(a|b|c))a?b?c?
Существует некоторая избыточность, но намного меньше, чем комбинаторный подход, который вы пытались избежать.