Регулярное выражение для соответствия сбалансированным скобкам - PullRequest
237 голосов
/ 13 февраля 2009

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

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

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

Ответы [ 16 ]

120 голосов
/ 13 февраля 2009

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

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

94 голосов
/ 08 ноября 2013

Вы можете использовать regex recursion :

\(([^()]|(?R))*\)
83 голосов
/ 08 февраля 2016

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


.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 .


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

27 голосов
/ 13 февраля 2009
[^\(]*(\(.*\))[^\)]*

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

15 голосов
/ 13 февраля 2009
(?<=\().*(?=\))

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

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


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

11 голосов
/ 23 сентября 2011

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

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

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

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

<
[^<>]*
(
    (
        (?<Open><)
        [^<>]*
    )+
    (
        (?<Close-Open>>)
        [^<>]*
    )+
)*
(?(Open)(?!))
>
7 голосов
/ 21 сентября 2017

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

<ч />

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

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

4 голосов
/ 15 мая 2012

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

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

)*
)
\)

Пример:

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

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

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

2 голосов
/ 08 июля 2016

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

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

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

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

String searchedString = str.substring(str1.indexOf('('),str1.lastIndexOf(')');
2 голосов
/ 02 августа 2014

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...