Регулярное выражение для соответствия сбалансированным скобкам

Мне нужно регулярное выражение, чтобы выделить весь текст в двух внешних скобках.

Пример: some text(text here(possible text)text(possible text(more text)))end text

Результат: (text here(possible text)text(possible text(more text)))

Ответ 1

Регулярные выражения являются неправильным инструментом для задания, потому что вы имеете дело с вложенными структурами, то есть с рекурсией.

Но для этого есть простой алгоритм, который я описал в этом ответе на предыдущем вопросе.

Ответ 3

Я хочу добавить этот ответ для быстрой ссылки. Не стесняйтесь обновлять.


.NET Regex с использованием балансировочных групп.

\((?>\((?<c>)|[^()]+|\)(?<-c>))*(?(c)(?!))\)

Где c используется в качестве счетчика глубины.

Демо на Regexstorm.com


PCRE с использованием рекурсивного шаблона.

\((?:[^)(]+|(?R))*+\)

Демо на regex101; Или без чередования:

\((?:[^)(]*(?R)?)*+\)

Демо на regex101; Или развернут для производительности:

\([^)(]*+(?:(?R)[^)(]*)*+\)

Демо на regex101; Шаблон вставляется в (?R) который представляет (?0).

Perl, PHP, Notepad++, R: perl = TRUE, Python: пакет Regex с (?V1) для поведения Perl.


Ruby с использованием вызовов подвыражений.

С Ruby 2.0 \g<0> можно использовать для вызова полного шаблона.

\((?>[^)(]+|\g<0>)*\)

Демонстрация в Rubular; Ruby 1.9 поддерживает только захват групповой рекурсии:

(\((?>[^)(]+|\g<1>)*\))

Демонстрация в Rubular (атомарная группировка начиная с Ruby 1.9.3)


JavaScript API :: XRegExp.matchRecursive

XRegExp.matchRecursive(str, '\\(', '\\)', 'g');

JS, Java и другие регулярные выражения без рекурсии до 2 уровней вложенности:

\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)

Демо на regex101. Более глубокое вложение должно быть добавлено к образцу.
Чтобы быстрее потерпеть неудачу при несбалансированных скобках, удалите квантификатор +.


Java: интересная идея с использованием прямых ссылок @jaytea.


Ссылка - Что означает это регулярное выражение?

Ответ 4

[^\(]*(\(.*\))[^\)]*

[^\(]* соответствует всем, что не является открывающей скобкой в ​​начале строки, (\(.*\)) фиксирует требуемую подстроку, заключенную в скобки, а [^\)]* соответствует всем, что не является закрывающей скобкой в ​​конце Струна. Обратите внимание, что это выражение не пытается сопоставить скобки; простой парсер (см. ответ дехмана) был бы более подходящим для этого.

Ответ 5

(?<=\().*(?=\))

Если вы хотите выбрать текст между двумя совпадающими скобками, вам не повезло с регулярными выражениями. Это невозможно (*).

Это регулярное выражение просто возвращает текст между первым открытием и последними закрывающимися скобками в вашей строке.


(*) Если ваш механизм регулярного выражения не имеет таких функций, как балансировочные группы или рекурсия. Количество двигателей, поддерживающих такие функции, медленно растет, но они по-прежнему не являются общедоступными.

Ответ 6

На самом деле это можно сделать с использованием регулярных выражений .NET, но это не так, но читайте внимательно.

Здесь вы можете прочитать хорошую статью здесь. Вам также может потребоваться прочитать регулярные выражения .NET. Вы можете начать читать здесь.

Угловые скобки <> использовались, потому что они не требуют экранирования.

Регулярное выражение выглядит следующим образом:

<
[^<>]*
(
    (
        (?<Open><)
        [^<>]*
    )+
    (
        (?<Close-Open>>)
        [^<>]*
    )+
)*
(?(Open)(?!))
>

Ответ 7

Этот ответ объясняет теоретическое ограничение того, почему регулярные выражения не являются подходящим инструментом для этой задачи.


Регулярные выражения не могут этого сделать.

Регулярные выражения основаны на вычислительной модели, известной как Finite State Automata (FSA). Как видно из названия, FSA может запомнить только текущее состояние, у него нет информации о предыдущих состояниях.

FSA

На приведенной выше схеме S1 и S2 - это два состояния, где S1 - начальный и последний этап. Так что, если мы попробуем со строкой 0110, переход будет выглядеть следующим образом:

      0     1     1     0
-> S1 -> S2 -> S2 -> S2 ->S1

На вышеприведенных этапах, когда мы находимся на втором S2 т.е. после анализа 01 из 0110, FSA не имеет информации о предыдущих 0 в 01 поскольку он может только запомнить текущее состояние и следующий входной символ.

В вышеупомянутой проблеме нам нужно знать номер открывающей скобки; это означает, что он должен храниться в каком-то месте. Но поскольку FSAs не может этого сделать, регулярное выражение не может быть написано.

Однако для этой задачи можно написать алгоритм. Алгоритмы обычно подпадают под Pushdown Automata (PDA). PDA на один уровень выше FSA. КПК имеет дополнительный стек для хранения дополнительной информации. КПК могут быть использованы для решения вышеуказанной проблемы, потому что мы можем " push " открытие скобка в стеке и " pop " их, когда мы столкнулись с закрывающей скобкой. Если в конце стек пуст, то открывающая и закрывающая скобки совпадают. В противном случае нет.

Ответ 8

Это окончательное регулярное выражение:

\(
(?<arguments> 
(  
  ([^\(\)']*) |  
  (\([^\(\)']*\)) |
  '(.*?)'

)*
)
\)

Пример:

input: ( arg1, arg2, arg3, (arg4), '(pip' )

output: arg1, arg2, arg3, (arg4), '(pip'

обратите внимание, что '(pip' правильно управляется как строка. (проверено в регуляторе: http://sourceforge.net/projects/regulator/)

Ответ 9

Регулярное выражение с использованием Ruby (версия 1.9.3 или выше):

/(?<match>\((?:\g<match>|[^()]++)*\))/

Демо на рублевом

Ответ 10

Я написал небольшую библиотеку JavaScript под названием сбалансированный, чтобы помочь с этой задачей. Вы можете сделать это, выполнив

balanced.matches({
    source: source,
    open: '(',
    close: ')'
});

Вы даже можете сделать замены:

balanced.replacements({
    source: source,
    open: '(',
    close: ')',
    replace: function (source, head, tail) {
        return head + source + tail;
    }
});

Вот более сложный и интерактивный пример JSFiddle.

Ответ 11

Вам нужны первые и последние скобки. Используйте что-то вроде этого:

str.indexOf('('); - это даст вам первое вхождение

str.lastIndexOf( ')'); - последний

Итак, вам нужна строка между,

String searchedString = str.substring(str1.indexOf('('),str1.lastIndexOf(')');

Ответ 12

В добавление к ответу "всплывающее окно" есть и другие варианты регулярных выражений, в которых поддерживаются рекурсивные конструкции.

Lua

Используйте %b() (%b{}/%b[] для фигурных скобок/квадратных скобок):

Perl6:

Неперекрывающиеся совпадения с несколькими сбалансированными скобками:

my regex paren_any { '(' ~ ')' [ <-[()]>+ || <&paren_any> ]* }
say "Extract (a(b)c) and ((d)f(g))" ~~ m:g/<&paren_any>/;
# => (「(a(b)c)」 「((d)f(g))」)

Перекрывающиеся совпадения с несколькими сбалансированными скобками:

say "Extract (a(b)c) and ((d)f(g))" ~~ m:ov:g/<&paren_any>/;
# => (「(a(b)c)」 「(b)」 「((d)f(g))」 「(d)」 「(g)」)

Смотрите демо.

Python re решение без регулярных выражений

Смотрите poke answer для Как получить выражение между сбалансированными скобками.

Java настраиваемое решение без регулярных выражений

Вот настраиваемое решение, разрешающее использование односимвольных литералов в Java:

public static List<String> getBalancedSubstrings(String s, Character markStart, 
                                 Character markEnd, Boolean includeMarkers) 

{
        List<String> subTreeList = new ArrayList<String>();
        int level = 0;
        int lastOpenDelimiter = -1;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c == markStart) {
                level++;
                if (level == 1) {
                    lastOpenDelimiter = (includeMarkers ? i : i + 1);
                }
            }
            else if (c == markEnd) {
                if (level == 1) {
                    subTreeList.add(s.substring(lastOpenDelimiter, (includeMarkers ? i + 1 : i)));
                }
                if (level > 0) level--;
            }
        }
        return subTreeList;
    }
}

Пример использования:

String s = "some text(text here(possible text)text(possible text(more text)))end text";
List<String> balanced = getBalancedSubstrings(s, '(', ')', true);
System.out.println("Balanced substrings:\n" + balanced);
// => [(text here(possible text)text(possible text(more text)))]

Ответ 13

Ответ зависит от того, нужно ли вам сопоставлять совпадающие наборы скобок или просто сначала открывать до последнего закрытия входного текста.

Если вам нужно сопоставить вложенные скобки, вам нужно что-то большее, чем регулярные выражения. - см. @dehmann

Если он сначала открывается для закрытия, см. @Zach

Решите, с чем хотите:

abc ( 123 ( foobar ) def ) xyz ) ghij

Вам нужно решить, что ваш код должен соответствовать в этом случае.

Ответ 14

"""
Here is a simple python program showing how to use regular
expressions to write a paren-matching recursive parser.

This parser recognises items enclosed by parens, brackets,
braces and <> symbols, but is adaptable to any set of
open/close patterns.  This is where the re package greatly
assists in parsing. 
"""

import re


# The pattern below recognises a sequence consisting of:
#    1. Any characters not in the set of open/close strings.
#    2. One of the open/close strings.
#    3. The remainder of the string.
# 
# There is no reason the opening pattern can't be the
# same as the closing pattern, so quoted strings can
# be included.  However quotes are not ignored inside
# quotes.  More logic is needed for that....


pat = re.compile("""
    ( .*? )
    ( \( | \) | \[ | \] | \{ | \} | \< | \> |
                           \' | \" | BEGIN | END | $ )
    ( .* )
    """, re.X)

# The keys to the dictionary below are the opening strings,
# and the values are the corresponding closing strings.
# For example "(" is an opening string and ")" is its
# closing string.

matching = { "(" : ")",
             "[" : "]",
             "{" : "}",
             "<" : ">",
             '"' : '"',
             "'" : "'",
             "BEGIN" : "END" }

# The procedure below matches string s and returns a
# recursive list matching the nesting of the open/close
# patterns in s.

def matchnested(s, term=""):
    lst = []
    while True:
        m = pat.match(s)

        if m.group(1) != "":
            lst.append(m.group(1))

        if m.group(2) == term:
            return lst, m.group(3)

        if m.group(2) in matching:
            item, s = matchnested(m.group(3), matching[m.group(2)])
            lst.append(m.group(2))
            lst.append(item)
            lst.append(matching[m.group(2)])
        else:
            raise ValueError("After <<%s %s>> expected %s not %s" %
                             (lst, s, term, m.group(2)))

# Unit test.

if __name__ == "__main__":
    for s in ("simple string",
              """ "double quote" """,
              """ 'single quote' """,
              "one'two'three'four'five'six'seven",
              "one(two(three(four)five)six)seven",
              "one(two(three)four)five(six(seven)eight)nine",
              "one(two)three[four]five{six}seven<eight>nine",
              "one(two[three{four<five>six}seven]eight)nine",
              "oneBEGINtwo(threeBEGINfourENDfive)sixENDseven",
              "ERROR testing ((( mismatched ))] parens"):
        print "\ninput", s
        try:
            lst, s = matchnested(s)
            print "output", lst
        except ValueError as e:
            print str(e)
    print "done"

Ответ 15

Этот тоже работал

re.findall(r'\(.+\)', s)

Ответ 16

Это может быть полезно для некоторых:

Разбор параметров из строки функции (с вложенными структурами) в JavaScript

Структуры соответствия, такие как:
Parse parmeters from function string

  • соответствует скобкам, квадратным скобкам, скобкам, одинарным и двойным кавычкам

Здесь вы можете увидеть сгенерированное регулярное выражение в действии

/**
 * get param content of function string.
 * only params string should be provided without parentheses
 * WORK even if some/all params are not set
 * @return [param1, param2, param3]
 */
exports.getParamsSAFE = (str, nbParams = 3) => {
    const nextParamReg = /^\s*((?:(?:['"([{](?:[^'"()[\]{}]*?|['"([{](?:[^'"()[\]{}]*?|['"([{][^'"()[\]{}]*?['")}\]])*?['")}\]])*?['")}\]])|[^,])*?)\s*(?:,|$)/;
    const params = [];
    while (str.length) { // this is to avoid a BIG performance issue in javascript regexp engine
        str = str.replace(nextParamReg, (full, p1) => {
            params.push(p1);
            return '';
        });
    }
    return params;
};

Это не в полной мере отвечает на вопрос OP, но я думаю, что некоторые могут прийти сюда для поиска регулярных выражений вложенной структуры.