Я не согласен с тем, что регулярные выражения являются неправильным инструментом для этого по нескольким причинам. 1) Большинство реализаций регулярных выражений имеют работоспособное, если не идеальное решение для этого. 2) Часто вы пытаетесь найти сбалансированные пары разделителей в контексте, где также используются другие критерии, хорошо подходящие для регулярных выражений. 3) Часто вы передаете регулярное выражение в некоторый API, который принимает только регулярные выражения, и у вас нет выбора.
Regex - ПРАВИЛЬНЫЙ инструмент для работы. Этот ответ не правильный. Смотрите ответ rogal111.
Андрей
4
Абсолютно согласен с ответом. Хотя в regexp есть некоторые реализации рекурсии, они равны конечным автоматам и не предназначены для работы с вложенными структурами, но это делают контекстно-свободные грамматики. Посмотрите на иерархию формальных грамматик Хомского.
Ник Роз
139
Я хочу добавить этот ответ для быстрой ссылки. Не стесняйтесь обновлять.
Когда вы повторяете группу с притяжательным квантификатором, делать эту группу атомарной бесполезно, поскольку все позиции возврата в этой группе удаляются при каждом повторении. Так что письмо (?>[^)(]+|(?R))*+- это то же самое, что и письмо (?:[^)(]+|(?R))*+. То же самое для следующего шаблона. Что касается развернутой версии, вы можете поместить здесь собственнический квантификатор: [^)(]*+для предотвращения возврата (если нет закрывающей скобки).
Казимир и Ипполит
Что касается шаблона Ruby 1.9, то вместо того, чтобы делать повторяющуюся группу атомарной (которая имеет ограниченный интерес, когда много вложенных скобок (...(..)..(..)..(..)..(..)..)) в строке темы), вы можете использовать простую группу без захвата и заключить все в атомарную группу: (?>(?:[^)(]+|\g<1>)*)( это ведет себя точно так же как притяжательный квантификатор). В Ruby 2.x доступен квантификатор притяжений.
Казимир и Ипполит
@CasimiretHippolyte Спасибо! Я настроил шаблоны PCRE и для Ruby 1.9, вы хотите сказать, что весь шаблон будет таким ? Пожалуйста, не стесняйтесь обновлять себя. Я понимаю, что вы имеете в виду, но не уверен, что есть много улучшений.
Пример был бы очень полезен здесь, я не могу заставить это работать для таких вещей, как "(1, (2, 3)) (4, 5)".
Энди Хейден
5
@AndyHayden это потому, что "(1, (2, 3)) (4, 5)" имеет две группы, разделенные пробелом. Используйте мое регулярное выражение с глобальным флагом: / (([^ ()] | (? R)) *) / g. Вот онлайн тест: regex101.com/r/lF0fI1/1
В .NET 4.5 Я получаю следующее сообщение об ошибке для этой модели: Unrecognized grouping construct.
Nam
4
Потрясающие! Это отличная особенность регулярных выражений. Спасибо, что вы единственный, кто действительно ответил на вопрос. Кроме того, этот сайт regex101 хорош.
Андрей
28
[^\(]*(\(.*\))[^\)]*
[^\(]*сопоставляет все, что не является открывающей скобкой в начале строки, (\(.*\))захватывает требуемую подстроку, заключенную в скобки, и [^\)]*сопоставляет все, что не является закрывающей скобкой в конце строки. Обратите внимание, что это выражение не пытается сопоставить скобки; простой парсер (см . ответ Дехмана ) был бы более подходящим для этого.
скобка внутри класса не нуждается в экранировании. Так как внутри это не метасимвол.
Хосе Лил
10
Этот expr не работает с чем-то вроде «text (text) text (text) text« return »(text) text (text)». Регулярные выражения не могут считать скобки.
Кристиан Клаузер
17
(?<=\().*(?=\))
Если вы хотите выбрать текст в двух совпадающих скобках, вам не повезло с регулярными выражениями. Это невозможно (*) .
Это регулярное выражение просто возвращает текст между первым открывающим и последним закрывающими скобками в вашей строке.
(*) Если ваш движок регулярных выражений не имеет таких функций, как балансировка групп или рекурсия . Количество движков, поддерживающих такие функции, медленно растет, но они все еще не являются общедоступными.
Что означают знаки "<=" и "="? На какой механизм регулярных выражений нацелено это выражение?
Кристиан Клаузер
1
Это осмотр, или, точнее, «утверждения упреждения / упущения нулевой ширины». Большинство современных двигателей регулярных выражений поддерживают их.
Томалак
Согласно примеру ОП, он хочет включить в матч самые отдаленные парены. Это регулярное выражение выбрасывает их.
Алан Мур
1
@ Алан М: Вы правы. Но согласно тексту вопроса, он хочет, чтобы все между самыми внешними паренями. Выберите свой выбор. Он сказал, что пытался часами, поэтому даже не рассматривал «все, включая самых крайних парней», как намерение, потому что это так тривиально: «(. *)».
Томалак
3
@ghayes Ответ с 2009 года. Это давно ; механизмы регулярных выражений, которые допускают некоторую форму рекурсии, были более необычными, чем сейчас (и они все еще довольно редки). Я упомяну это в своем ответе.
Томалак
14
Этот ответ объясняет теоретическое ограничение того, почему регулярные выражения не являются подходящим инструментом для этой задачи.
Регулярные выражения не могут этого сделать.
Регулярные выражения основаны на вычислительной модели, известной как Finite State Automata (FSA). Как видно из названия, a FSAможет запомнить только текущее состояние, в нем нет информации о предыдущих состояниях.
На приведенной выше диаграмме S1 и S2 представляют собой два состояния, где S1 является начальным и последним этапом. Так что, если мы попробуем со строкой 0110, переход будет следующим:
0110-> S1 -> S2 -> S2 -> S2 ->S1
В приведенных выше шагов, когда мы находимся на втором , S2т.е. после разбора 01из 0110, АФН не имеет никакой информации о предыдущей 0в 01как он может вспомнить только текущее состояние и следующий входной символ.
В вышеупомянутой проблеме нам нужно знать номер открывающей скобки; это означает, что он должен храниться в каком-то месте. Но так как FSAsне может этого сделать, регулярное выражение не может быть написано.
Однако для этой задачи можно написать алгоритм. Алгоритмы, как правило, подпадают под Pushdown Automata (PDA). PDAна один уровень выше FSA. КПК имеет дополнительный стек для хранения дополнительной информации. КПК могут быть использованы для решения вышеуказанной проблемы, потому что мы можем « pushоткрывать круглые скобки в стеке» и pop«их», когда встречаем закрывающие скобки. Если в конце стек пуст, то открывающая и закрывающая скобки совпадают. В противном случае нет.
Здесь есть несколько ответов, которые доказывают, что это возможно.
Иржи Эрник,
1
@Marco Этот ответ говорит о регулярных выражениях в теоретической перспективе. Многие движки регулярных выражений в наши дни не только полагаются на эту теоретическую модель и используют некоторую дополнительную память для своей работы!
Musibs
@ JiříHerník: это не регулярные выражения в строгом смысле: не определяется как регулярные выражения Клини . Некоторые механизмы регулярных выражений действительно реализовали некоторые дополнительные возможности, благодаря чему они могут анализировать не только обычные языки .
Виллем Ван Онсем
12
На самом деле это возможно сделать с помощью регулярных выражений .NET, но это не тривиально, поэтому читайте внимательно.
Вы можете прочитать хорошую статью здесь . Вам также может понадобиться прочитать регулярные выражения .NET. Вы можете начать читать здесь .
Угловые скобки <>были использованы, потому что они не требуют побега.
"""
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 =[]whileTrue:
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:raiseValueError("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
exceptValueErroras e:print str(e)print"done"
Хотя многие ответы упоминают об этом в той или иной форме, говоря, что регулярное выражение не поддерживает рекурсивное сопоставление и т. Д., Основная причина этого кроется в корнях теории вычислений.
Язык формы {a^nb^n | n>=0} is not regular . Regex может соответствовать только вещам, которые являются частью обычного набора языков.
Я не использовал регулярные выражения, так как трудно иметь дело с вложенным кодом. Таким образом, этот фрагмент должен быть в состоянии позволить вам захватывать фрагменты кода со сбалансированными скобками:
def extract_code(data):""" returns an array of code snippets from a string (data)"""
start_pos =None
end_pos =None
count_open =0
count_close =0
code_snippets =[]for i,v in enumerate(data):if v =='{':
count_open+=1ifnot start_pos:
start_pos= i
if v=='}':
count_close +=1if count_open == count_close andnot end_pos:
end_pos = i+1if start_pos and end_pos:
code_snippets.append((start_pos,end_pos))
start_pos =None
end_pos =Nonereturn code_snippets
Я использовал это для извлечения фрагментов кода из текстового файла.
/**
* 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*(?:,|$)/;constparams=[];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'';});}returnparams;};
Это не в полной мере отвечает на вопрос OP, но я думаю, что это может быть полезным для некоторых, приходящих сюда для поиска регулярных выражений вложенной структуры.
Ответы:
Регулярные выражения являются неподходящим инструментом для работы, потому что вы имеете дело с вложенными структурами, то есть рекурсией.
Но для этого есть простой алгоритм, который я описал в ответе на предыдущий вопрос .
источник
Я хочу добавить этот ответ для быстрой ссылки. Не стесняйтесь обновлять.
.NET Regex с использованием балансировочных групп .
Где
c
используется в качестве счетчика глубины.Демо на Regexstorm.com
PCRE с использованием рекурсивного шаблона .
Демо на regex101 ; Или без чередования:
Демо на regex101 ; Или развернут для производительности:
Демо на regex101 ; Узор наклеен на
(?R)
который представляет(?0)
.Perl, PHP, Notepad ++, R : perl = TRUE , Python : Regex пакет с
(?V1)
для поведения Perl.Использование Ruby вызовов подвыражений .
С Ruby 2.0
\g<0>
можно использовать для вызова полного шаблона.Демонстрация в Rubular ; Ruby 1.9 поддерживает только групповую рекурсию :
Демо в Rubular ( атомная группировка начиная с Ruby 1.9.3)
JavaScript API :: XRegExp.matchRecursive
JS, Java и другие регулярные выражения без рекурсии до 2 уровней вложенности:
Демонстрация на regex101 . Более глубокое вложение должно быть добавлено к образцу.
Чтобы быстрее потерпеть неудачу при несбалансированной скобке, отбросьте
+
квантификатор.Java : интересная идея с использованием прямых ссылок @jaytea .
Ссылка - Что означает это регулярное выражение?
источник
(?>[^)(]+|(?R))*+
- это то же самое, что и письмо(?:[^)(]+|(?R))*+
. То же самое для следующего шаблона. Что касается развернутой версии, вы можете поместить здесь собственнический квантификатор:[^)(]*+
для предотвращения возврата (если нет закрывающей скобки).(...(..)..(..)..(..)..(..)..)
) в строке темы), вы можете использовать простую группу без захвата и заключить все в атомарную группу:(?>(?:[^)(]+|\g<1>)*)
( это ведет себя точно так же как притяжательный квантификатор). В Ruby 2.x доступен квантификатор притяжений.Вы можете использовать регулярные выражения :
источник
Unrecognized grouping construct
.[^\(]*
сопоставляет все, что не является открывающей скобкой в начале строки,(\(.*\))
захватывает требуемую подстроку, заключенную в скобки, и[^\)]*
сопоставляет все, что не является закрывающей скобкой в конце строки. Обратите внимание, что это выражение не пытается сопоставить скобки; простой парсер (см . ответ Дехмана ) был бы более подходящим для этого.источник
Если вы хотите выбрать текст в двух совпадающих скобках, вам не повезло с регулярными выражениями. Это невозможно (*) .
Это регулярное выражение просто возвращает текст между первым открывающим и последним закрывающими скобками в вашей строке.
(*) Если ваш движок регулярных выражений не имеет таких функций, как балансировка групп или рекурсия . Количество движков, поддерживающих такие функции, медленно растет, но они все еще не являются общедоступными.
источник
Этот ответ объясняет теоретическое ограничение того, почему регулярные выражения не являются подходящим инструментом для этой задачи.
Регулярные выражения не могут этого сделать.
Регулярные выражения основаны на вычислительной модели, известной как
Finite State Automata (FSA)
. Как видно из названия, aFSA
может запомнить только текущее состояние, в нем нет информации о предыдущих состояниях.На приведенной выше диаграмме S1 и S2 представляют собой два состояния, где S1 является начальным и последним этапом. Так что, если мы попробуем со строкой
0110
, переход будет следующим:В приведенных выше шагов, когда мы находимся на втором ,
S2
т.е. после разбора01
из0110
, АФН не имеет никакой информации о предыдущей0
в01
как он может вспомнить только текущее состояние и следующий входной символ.В вышеупомянутой проблеме нам нужно знать номер открывающей скобки; это означает, что он должен храниться в каком-то месте. Но так как
FSAs
не может этого сделать, регулярное выражение не может быть написано.Однако для этой задачи можно написать алгоритм. Алгоритмы, как правило, подпадают под
Pushdown Automata (PDA)
.PDA
на один уровень вышеFSA
. КПК имеет дополнительный стек для хранения дополнительной информации. КПК могут быть использованы для решения вышеуказанной проблемы, потому что мы можем «push
открывать круглые скобки в стеке» иpop
«их», когда встречаем закрывающие скобки. Если в конце стек пуст, то открывающая и закрывающая скобки совпадают. В противном случае нет.источник
На самом деле это возможно сделать с помощью регулярных выражений .NET, но это не тривиально, поэтому читайте внимательно.
Вы можете прочитать хорошую статью здесь . Вам также может понадобиться прочитать регулярные выражения .NET. Вы можете начать читать здесь .
Угловые скобки
<>
были использованы, потому что они не требуют побега.Регулярное выражение выглядит так:
источник
Это окончательное регулярное выражение:
Пример:
обратите внимание, что
'(pip'
правильно управляется как строка. (пробовал в регуляторе: http://sourceforge.net/projects/regulator/ )источник
Я написал небольшую библиотеку JavaScript под названием сбалансированный, чтобы помочь с этой задачей. Вы можете сделать это, выполнив
Вы даже можете сделать замены:
Вот более сложный и интерактивный пример JSFiddle .
источник
В добавление к ответу «всплывающего пузыря» есть и другие варианты регулярных выражений, в которых поддерживаются рекурсивные конструкции.
Lua
Используйте
%b()
(%b{}
/%b[]
для фигурных скобок / квадратных скобок):for s in string.gmatch("Extract (a(b)c) and ((d)f(g))", "%b()") do print(s) end
(см. демонстрацию )Perl6 :
Неперекрывающиеся совпадения с несколькими сбалансированными скобками:
Перекрывающиеся совпадения с несколькими сбалансированными скобками:
Смотрите демо .
Python
re
решение без регулярных выраженийСм ответ тыкать в для Как получить выражение между сбалансированными скобками .
Java настраиваемое решение без регулярных выражений
Вот настраиваемое решение, разрешающее использование односимвольных буквенных разделителей в Java:
Пример использования:
источник
Регулярное выражение с использованием Ruby (версия 1.9.3 или выше):
Демо на Рубларе
источник
Вам нужны первые и последние скобки. Используйте что-то вроде этого:
str.indexOf ('('); - это даст вам первое вхождение
str.lastIndexOf ( ')'); - последний
Итак, вам нужна строка между,
источник
источник
Ответ зависит от того, нужно ли вам сопоставлять совпадающие наборы скобок или просто от первого открытия до последнего закрытия во входном тексте.
Если вам нужно сопоставить совпадающие вложенные скобки, вам нужно нечто большее, чем регулярные выражения. - см @dehmann
Если это только сначала открыт, чтобы в последний раз увидеть @Zach
Решите, что вы хотите с:
Вы должны решить, что ваш код должен соответствовать в этом случае.
источник
Поскольку регулярное выражение js не поддерживает рекурсивное сопоставление, я не могу сделать сопоставление в скобках.
так что это простой javascript для версии цикла, который превращает строку «method (arg)» в массив
результат как
источник
Хотя многие ответы упоминают об этом в той или иной форме, говоря, что регулярное выражение не поддерживает рекурсивное сопоставление и т. Д., Основная причина этого кроется в корнях теории вычислений.
Язык формы
{a^nb^n | n>=0} is not regular
. Regex может соответствовать только вещам, которые являются частью обычного набора языков.Читать дальше @ здесь
источник
Я не использовал регулярные выражения, так как трудно иметь дело с вложенным кодом. Таким образом, этот фрагмент должен быть в состоянии позволить вам захватывать фрагменты кода со сбалансированными скобками:
Я использовал это для извлечения фрагментов кода из текстового файла.
источник
Я также застрял в этой ситуации, когда появляются вложенные шаблоны.
Регулярное выражение - правильная вещь для решения вышеуказанной проблемы. Используйте ниже шаблон
источник
Этот тоже работал
источник
Это может быть полезно для некоторых:
Разбор параметров из строки функции (с вложенными структурами) в JavaScript
Структуры соответствия, такие как:
Здесь вы можете увидеть сгенерированное регулярное выражение в действии
Это не в полной мере отвечает на вопрос OP, но я думаю, что это может быть полезным для некоторых, приходящих сюда для поиска регулярных выражений вложенной структуры.
источник