2016-09-26 4 views
1

Я хотел бы сопоставить параметры любой функции в виде строки с использованием регулярного выражения. В качестве примера предположим следующую строку:Regex Match равное количество двух символов

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.

+0

[этот ответ] (http://stackoverflow.com/a/24696831/2272638) может привести в правом направление?Недостаточно кофе, чтобы доказать/адаптироваться, иначе я бы опубликовал ответ :). – dwanderson

+0

Модуль 're' Python не может обрабатывать рекурсию или балансировку, которые вам понадобятся здесь. Можете ли вы установить модуль '' regex' (PyPI)] (https://pypi.python.org/pypi/regex)? –

+0

@dwanderson, который, к сожалению, не работает (или я не получаю id), поскольку он не пытается сопоставить равное количество скобок, он просто сопоставляет что-либо между a (и a) – weidler

ответ

1

Добавление PyPI package regex, как предложено @Tim Pietzcker, вы можете использовать recursive regexes.

>>> 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')] 
+0

большое спасибо! Является ли рекурсия такой же простой, как вызов группы с (? 2) сам по себе? – weidler

+0

Да. Вам просто нужно убедиться, что группа содержит все, что вы хотите повторить. См. Http://www.regular-expressions.info/recurse.html для получения дополнительной информации. –

1
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'])] 

возвращает список всех разделенных запятыми выражений верхнего уровня. Вызов функций становится кортежем имени и аргументов.

+0

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

1

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

([_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', '...') 
] 

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

+0

так же, как и с ответом Маркуса Гардероса: его способ, которым я уже занимался, но я не хотел использовать столько кода, надеясь, что это можно было бы решить его с помощью регулярного выражения, тем более что это может повысить производительность – weidler

Смежные вопросы