Использование 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)"

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

Ответ 1

Регулярные выражения могут определенно соответствовать сбалансированным скобкам. Это может быть сложно, и требуется несколько более сложных функций 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 была очень полезной.

Ответ 2

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

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>\)) являются Balancing Group (BR я использовал для имени для Brackets). Таким образом, более ясно, что (?<BR> \( )|(?<-BR> \) ) возможно, так что \( и \) более "очевидны",.

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

Ответ 3

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

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

Ответ 4

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

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

Эта часть, которую вы уже знаете:

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

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

(([^()])|(\([^()]*\)))*