Использование RegEx для балансировки круглых скобок

Я пытаюсь создать выражение.NET RegEx, которое будет правильно сбалансировать мои скобки. У меня есть следующее выражение RegEx:

func([a-zA-Z_][a-zA-Z0-9_]*)\(.*\)

Вот строка, которую я пытаюсь найти:

"test -> funcPow((3),2) * (9+1)"

Что должно произойти, Regex должен соответствовать всем от funcPow до второй закрывающей скобки. Он должен остановиться после второй закрывающей скобки. Вместо этого он полностью соответствует последней закрывающей скобке. RegEx возвращает это:

"funcPow((3),2) * (9+1)"

Это должно вернуть это:

"funcPow((3),2)"

Любая помощь в этом будет оценена.

4 ответа

Решение

Регулярные выражения могут определенно соответствовать сбалансированным скобкам. Это может быть сложно, и требует нескольких более продвинутых функций Regex, но это не слишком сложно.

Пример:

var r = new Regex(@"
    func([a-zA-Z_][a-zA-Z0-9_]*) # The func name

    \(                      # First '('
        (?:                 
        [^()]               # Match all non-braces
        |
        (?<open> \( )       # Match '(', and capture into 'open'
        |
        (?<-open> \) )      # Match ')', and delete the 'open' capture
        )+
        (?(open)(?!))       # Fails if 'open' stack isn't empty!

    \)                      # Last ')'
", RegexOptions.IgnorePatternWhitespace);

Сбалансированные группы соответствия имеют несколько функций, но для этого примера мы используем только функцию удаления захвата. Линия (?<-open> \) ) будет соответствовать ) и удалите предыдущий "открытый" захват.

Самая хитрая линия (?(open)(?!))Итак, позвольте мне объяснить это. (?(open) является условным выражением, которое соответствует только при наличии "открытого" захвата. (?!) это негативное выражение, которое всегда терпит неудачу. Следовательно, (?(open)(?!)) говорит "если есть открытый захват, то провал".

Документация Microsoft тоже была довольно полезной.

Используя сбалансированные группы, это:

Regex rx = new Regex(@"func([a-zA-Z_][a-zA-Z0-9_]*)\(((?<BR>\()|(?<-BR>\))|[^()]*)+\)");

var match = rx.Match("funcPow((3),2) * (9+1)");

var str = match.Value; // funcPow((3),2)

(?<BR>\()|(?<-BR>\)) балансирующая группа (BR Я использовал для имени для Brackets). Так понятнее (?<BR> \ ()|(?<-BR>\) ) возможно, чтобы \( а также \) более "очевидны".

Если вы действительно ненавидите себя (и мир / ваших коллег по программированию) достаточно, чтобы использовать эти вещи, я предлагаю использовать RegexOptions.IgnorePatternWhitespace и "разбрызгивание" пустого пространства везде:-)

Регулярные выражения работают только на регулярных языках. Это означает, что регулярное выражение может находить вещи типа "любая комбинация a и b". (ab или же babbabaaa и т. д.) Но они не могут найти "н а, б, н а". (a^n b a^n) Регулярные выражения не могут гарантировать, что первый набор а соответствует второму набору а.

Из-за этого они не могут совпадать с одинаковыми числами открывающей и закрывающей скобок. Было бы достаточно легко написать функцию, которая перебирает строку по одному символу за раз. Есть два счетчика, один для открытия парен, другой для закрытия. увеличивайте указатели при прохождении строки, если opening_paren_count != closing_parent_count вернуть ложь.

func[a-zA-Z0-9_]*\((([^()])|(\([^()]*\)))*\)

Вы можете использовать это, но если вы работаете с.NET, могут быть лучшие альтернативы.

Эту часть вы уже знаете:

 func[a-zA-Z0-9_]*\( --weird part-- \)

- странная часть - часть просто означает; ( разрешить любой персонаж ., или же | любой раздел (.*) существовать столько раз, сколько захочет )*, Единственная проблема в том, что вы не можете подобрать ни одного персонажа ., вы должны использовать [^()] исключить скобки.

(([^()])|(\([^()]*\)))*
Другие вопросы по тегам