C# регулярное выражение с балансировочными группами не отвечает
У меня есть следующий код:
void Main()
{
string template = @"
aaa
{begin iteration items}
bbbbbb
{begin iteration subitems}
ccccccc
{end iteration subitems}
ddddddddd
{begin iteration items}
hhhhhhhhhhhhhhhhh
{end iteration items}
iiiiiiiiiiiiiiiiiiiiiiiiiiii
{end iteration items}
eeeeeeeeeeeeeeee
{begin iteration items}
ffffff
{end iteration items}
gggggggggggg
";
string re = @"
\{\s*begin\s+iteration\s+items\s*}
(?<template>
(
(?<iteration>\{\s*begin\s+iteration\s+items\s*})
|(?<-iteration>\{\s*end\s+iteration\s+items\s*})
|((?!(\{\s*begin\s+iteration\s+items\s*})|(\{\s*end\s+iteration\s+items\s*})).*?)
)*(?(iteration)(?!))
)
\{\s*end\s+iteration\s+items\s*}
";
Regex r = new Regex(re, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
var matches = r.Matches(template);
matches.Dump();
}
когда template
Сбалансировано, тогда совпадения возвращаются, и все в порядке. Но когда я меняюсь {end iteration items}
в {end1 iteration items}
после iiiiiiiiiiiiiii
строка в шаблоне, то код перестает отвечать на matches.Dump()
линия (Dump()
метод расширения для чтения / перечисления в LinQPad)
Что случилось? Можно ли переписать Regex, чтобы он всегда отвечал?
РЕДАКТИРОВАТЬ Моя цель состоит в том, чтобы захватить весь верхний уровень <template>
группы, если синтаксис действителен, или ничего не захватывать, если нет. Я попробовал группы без возврата, как советовал Лукас, но сейчас нет никаких перехватов, когда синтаксис действителен.
1 ответ
Вы испытываете катастрофическое отступление здесь.
Короче говоря: шаблон в виде ((something)*)*
с вложенными квантификаторами вызовет его, потому что движок должен попробовать все возможные комбинации, если совпадение не может быть найдено сразу.
Вы можете использовать атомную группу для защиты от нее. Следующее должно сделать трюк:
\{\s*begin\s+iteration\s+items\s*}
(?<template>
(?>
(?<iteration>\{\s*begin\s+iteration\s+items\s*})
|(?<-iteration>\{\s*end\s+iteration\s+items\s*})
|[^{]+
|\{
)*(?(iteration)(?!))
)
\{\s*end\s+iteration\s+items\s*}
Или использовать ((?>
...))
вместо (?>
...)
если вам нужно захватить.
Я упростил выражение - больше не нужно заглядывать вперед при использовании атомарной группы, так как эти случаи будут обрабатываться iteration
групп. Последняя часть альтернативы (\{
) здесь для учета одиночных открывающих скобок, которые не являются частью последовательности начала / конца. Большая часть текста потребляется [^{]+
внутри атомной группы, поэтому возврат не может произойти.