Регулярное выражение соответствует равное количество двух символов
Я бы хотел сопоставить параметры любой функции в виде строки с помощью регулярных выражений. В качестве примера приведем следующую строку:
predicate(foo(x.bar, predicate(foo(...), bar)), bar)
это может быть частью более длинной последовательности
predicate(foo(x.bar, predicate(foo(...), bar)), bar)predicate(foo(x.bar, predicate(foo(...), bar)), bar)predicate(foo(x.bar, predicate(foo(...), bar)), bar)
Теперь я хочу найти все подстроки, которые представляют функцию / предикат и ее параметры (т.е. в первом примере всю строку, а также вложенные predicate(foo(...), bar)
). Проблема в том, что я не могу просто соответствовать так
predicate\(.*, bar\)
как я могу тогда соответствовать больше параметров предиката, если *
жадный или менее, если он ленивый. Это потому, что такие предикаты () могут быть вложенными.
Мне нужно регулярное выражение, которое находит строку predicate(...)
где ...
соответствует любой строке, которая содержит равное количество (
и )
х (ленивый)
Если это имеет значение: я использую регулярное выражение с модулем re в Python.
3 ответа
Добавляя регулярное выражение пакета PyPI, как предложил @Tim Pietzcker, вы можете использовать рекурсивные регулярные выражения.
>>> import regex
>>> s = 'predicate(foo(x.bar, predicate(foo(...), bar)), bar)'
>>> pattern = regex.compile(r'(\w+)(?=\(((?:\w+\((?2)\)|[^()])*)\))')
>>> pattern.findall(s)
[('predicate', 'foo(x.bar, predicate(foo(...), bar)), bar'),
('foo', 'x.bar, predicate(foo(...), bar)'),
('predicate', 'foo(...), bar'),
('foo', '...')]
Вы также можете ограничить поиск только "предикатом":
>>> pattern = regex.compile(r'(predicate)(?=\(((?:\w+\((?2)\)|[^()])*)\))')
>>> pattern.findall(s)
[('predicate', 'foo(x.bar, predicate(foo(...), bar)), bar'),
('predicate', 'foo(...), bar')]
Вы можете создать регулярное выражение, чтобы найти все вызовы функций в вашем коде. Что-то вроде этого:
([_a-zA-Z]+)(?=\()
Затем с помощью re
В модуле вы создаете структуру данных, индексирующую вызовы функций в вашем коде.
import re
code = 'predicate(foo(x.bar, predicate(foo(...), bar)), bar)predicate(foo(x.bar, predicate(foo(...), bar)), bar)predicate(foo(x.bar, predicate(foo(...), bar)), bar)'
code_cp = code
regex = re.compile(r'([_a-zA-Z]+)(?=\()')
matches = re.findall(regex, code)
structured_matches = []
for m in matches:
beg = str.index(code, m)
end = beg + len(m)
structured_matches.append((m, beg, end))
code = code[:beg] + '_' * len(m) + code[end:]
Это даст вам структуру данных, которая выглядит следующим образом:
[
('predicate', 0, 9),
('foo', 10, 13),
('predicate', 21, 30),
('foo', 31, 34),
('predicate', 52, 61),
('foo', 62, 65),
('predicate', 73, 82),
('foo', 83, 86),
('predicate', 104, 113),
('foo', 114, 117),
('predicate', 125, 134),
('foo', 135, 138)
]
Вы можете использовать эту структуру данных в сочетании с parse
функция для извлечения содержимого паренов каждого вызова функции.
def parse(string):
stack = []
contents = ''
opened = False
for c in string:
if len(stack) > 0:
contents += c
if c == '(':
opened = True
stack.append('o')
elif c == ')':
stack.pop()
if opened and len(stack) == 0:
break
return contents[:-1]
paren_contents = []
for m in structured_matches:
fn_name, beg, end = m
paren_contents.append((fn_name, parse(code_cp[end:])))
В конце, paren_contents
должен выглядеть примерно так:
[
('predicate', 'foo(x.bar, predicate(foo(...), bar)), bar'),
('foo', 'x.bar, predicate(foo(...), bar)'),
('predicate', 'foo(...), bar'), ('foo', '...'),
('predicate', 'foo(x.bar, predicate(foo(...), bar)), bar'),
('foo', 'x.bar, predicate(foo(...), bar)'),
('predicate', 'foo(...), bar'), ('foo', '...'),
('predicate', 'foo(x.bar, predicate(foo(...), bar)), bar'),
('foo', 'x.bar, predicate(foo(...), bar)'),
('predicate', 'foo(...), bar'),
('foo', '...')
]
Надеюсь, это указывает на правильное направление.
import re
def parse(s):
pattern = re.compile(r'([^(),]+)|\s*([(),])\s*')
stack = []
state = 0 # 0 = before identifier, 1 = after identifier, 2 = after closing paren
current = None
args = []
for match in pattern.finditer(s):
if match.group(1):
if state != 0:
raise SyntaxError("Expected identifier at {0}".format(match.start()))
current = match.group(1)
state = 1
elif match.group(2) == '(':
if state != 1:
raise SyntaxError("Unexpected open paren at {0}".format(match.start()))
stack.append((args, current))
state = 0
current = None
args = []
elif match.group(2) == ',':
if state != 0: args.append(current)
state = 0
current = None
elif match.group(2) == ')':
if state != 0: args.append(current)
if len(stack) == 0:
raise SyntaxError("Unmatched paren at {0}".format(match.start()))
newargs = args
args, current = stack.pop()
current = (current, newargs)
state = 2
if state != 0: args.append(current)
if len(stack) > 0:
raise SyntaxError("Unclosed paren")
return args
>>> from pprint import pprint
>>> pprint(parse('predicate(foo(x.bar, predicate(foo(...), bar)), bar)'), width=1)
[('predicate',
[('foo',
['x.bar',
('predicate',
[('foo',
['...']),
'bar'])]),
'bar'])]
Возвращает список всех выражений верхнего уровня, разделенных запятыми. Вызовы функций становятся кортежем имени и аргументов.