Регулярное выражение: кто жадник?
Моя главная проблема связана с Java-вкусом, но я также ценю информацию о других.
Скажем, у вас есть подшаблон:
(.*)(.*)
Не очень полезно, как есть, но пусть эти две группы захвата (например, \1
и \2
) являются частью более крупного шаблона, который соответствует обратным ссылкам этим группам и т.д.
Таким образом, оба жадные, поскольку они стараются захватить как можно больше, только принимая меньше, когда они должны.
Мой вопрос: кто жаднее? Возвращает ли \1
первостепенное значение, предоставляя \2
его долю только в том случае, если он должен?
Как насчет:
(.*)(.*)(.*)
Предположим, что \1
действительно имеет приоритет. Пусть говорят, что это слишком жадно, а потом выплевывает персонажа. Кто получает его первым? Всегда ли это \2
или может быть \3
?
Предположим, что он \2
, который получает отклонение \1
. Если это все еще не сработает, кто выплевывает сейчас? Перемещает ли \2
на \3
, или \1
сначала выплющает другое значение \2
?
Бонусный вопрос
Что произойдет, если вы напишете что-то вроде этого:
(.*)(.*?)(.*)
Теперь \2
неохотно. Означает ли это, что \1
выскакивает до \3
, а \2
только неохотно принимает отклонение \3
?
Пример
Возможно, для меня было ошибкой не приводить конкретные примеры, чтобы показать, как я использую эти шаблоны, но вот некоторые из них:
System.out.println(
"OhMyGod=MyMyMyOhGodOhGodOhGod"
.replaceAll("^(.*)(.*)(.*)=(\\1|\\2|\\3)+$", "<$1><$2><$3>")
); // prints "<Oh><My><God>"
// same pattern, different input string
System.out.println(
"OhMyGod=OhMyGodOhOhOh"
.replaceAll("^(.*)(.*)(.*)=(\\1|\\2|\\3)+$", "<$1><$2><$3>")
); // prints "<Oh><MyGod><>"
// now \2 is reluctant
System.out.println(
"OhMyGod=OhMyGodOhOhOh"
.replaceAll("^(.*)(.*?)(.*)=(\\1|\\2|\\3)+$", "<$1><$2><$3>")
); // prints "<Oh><><MyGod>"
Ответы
Ответ 1
Добавление ваших конкретных примеров сильно изменяет характер вопроса. Он все еще начинается, как я описал в своем первом ответе, с первым (.*)
, поглощающим все символы, а вторая и третья группы позволяют им иметь их, но тогда он должен соответствовать знаку равенства.
Очевидно, что в конце строки нет ни одного элемента, поэтому группа # 1 возвращает символы назад один за другим, пока =
в регулярном выражении не сможет соответствовать =
в целевом объекте. Затем двигатель regex начинает пытаться соответствовать (\1|\2|\3)+$
, и начинается настоящая забава.
Группа 1 отказывается от d
, а группа 2 (которая все еще пуста) принимает ее, но остальная часть регулярного выражения все еще не может совпадать. Группа 1 отдает значения o
и group 2 od
, но остальное регулярное выражение все еще не может совпадать. И так оно и происходит, с участием третьей группы, и три из них нарезают входной сигнал всеми возможными способами до достижения общего соответствия. RegexBuddy сообщает, что для этого требуется 13 426 шагов.
В первом примере жадность (или ее отсутствие) на самом деле не является фактором; единственный способ совпадения может заключаться в том, что слова Oh
, My
и God
фиксируются в отдельных группах, поэтому в итоге это происходит. Даже не имеет значения, какая группа фиксирует какое слово - это то, что только что произошло, прежде всего, как я сказал ранее.
Во втором и третьем примерах необходимо только разбить префикс на два куска: Oh
и MyGod
. Группа 2 захватывает MyGod
во втором примере, потому что она следующая в строке и она жадная, как в первом примере. В третьем примере каждый раз, когда группа 1 бросает символ, группа 2 (неохотно) позволяет группе 3 взять ее вместо этого, так что та, которая попадает во владение MyGod
.
Это сложнее (и утомительно), чем это, конечно, но я надеюсь, что это ответит на ваш вопрос. И я должен сказать, что интересная целевая строка, которую вы выбрали; если бы у механизма регулярных выражений был оргазм, я думаю, что эти регулярные выражения будут теми, кто его отключит.: D
Ответ 2
\1
будет иметь приоритет, \2
и \3
всегда ничего не будут соответствовать. \2
будет иметь приоритет над \3
.
Как правило, думайте об этом так: back-tracking будет только, чтобы соответствовать совпадению, это не произойдет, чтобы удовлетворить жадность, поэтому лучше всего:)
объяснение обратного отслеживания и жадности для меня очень важно, я бы предложил friedl Освоение регулярных выражений
Ответ 3
Квантеры по-настоящему не жадные по умолчанию, они просто поспешные. В вашем примере первый (.*)
начнется, поглощая все, что он может, независимо от потребностей регулярного выражения в целом. Только после этого он передает управление следующей части, и в случае необходимости он возвращает некоторые или все то, что он только что принял (т.е. Обратный путь), чтобы остальная часть регулярного выражения могла выполнять свою работу.
Это не обязательно в этом случае, потому что все остальное может юридически соответствовать нулевым символам. Если бы кванторы были действительно жадными, три группы будут торговаться, пока они не разделили вход как можно более равномерно; вместо этого вторая и третья группы позволяют первым сохранить то, что нужно. Они возьмут его, если он встанет перед ними, но они не будут бороться за это. (Это было бы правдой, даже если бы у них были притяжательные кванторы, т.е. (.*)(.*+)(.*+)
.)
Устранение второй точки-звезды не меняет ничего, но переключает первый. Недостаточный квантификатор начинается с сопоставления только столько, сколько ему нужно, а затем отрывается до следующей части. Итак, первая группа из (.*?)(.*)(.*)
начинается, ничего не сопоставляя, затем вторая группа поглощает все, а третья группа плачет "weee weee weee" до конца.
Вот бонусный вопрос для вас: что произойдет, если вы сделаете все три квантора неохотой? (Подсказка: на Java это вопрос API как вопрос регулярного выражения.)
Ответ 4
Регулярные выражения работают в последовательности, это означает, что регекс-оценщик оставит группу только тогда, когда он больше не сможет найти решение для этой группы, и, в конце концов, сделает некоторую обратную трассировку, чтобы строка соответствовала следующей группе. Если вы выполните это регулярное выражение, вы получите все ваши символы, которые будут оценены в первой группе, ни одна из следующих (вопрос-знак тоже не имеет значения).
Ответ 5
Как простое общее правило: выигрывает левый квантификатор. Поэтому, если следующие кванторы идентифицируют чисто необязательные подшаблоны (независимо от того, что они сделаны неровными), первый принимает все.