Соответствие регулярному выражению Python в условных выражениях - PullRequest
23 голосов
/ 04 мая 2011

Я анализирую файл и хочу проверить каждую строку на наличие нескольких сложных регулярных выражений. Как то так

if re.match(regex1, line): do stuff
elif re.match(regex2, line): do other stuff
elif re.match(regex3, line): do still more stuff
...

Конечно, чтобы делать вещи, мне нужны объекты соответствия. Я могу думать только о трех возможностях, каждая из которых оставляет желать лучшего.

if re.match(regex1, line): 
    m = re.match(regex1, line)
    do stuff
elif re.match(regex2, line):
    m = re.match(regex2, line)
    do other stuff
...

, который требует двойного сложного сопоставления (это длинные файлы и длинное регулярное выражение: /)

m = re.match(regex1, line)
if m: do stuff
else:
    m = re.match(regex2, line)
    if m: do other stuff
    else:
       ...

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

while True:
    m = re.match(regex1, line)
    if m:
        do stuff
        break
    m = re.match(regex2, line)
    if m:
        do other stuff
        break
    ...

, что выглядит странно.

Какой правильный способ сделать это?

Ответы [ 12 ]

15 голосов
/ 04 мая 2011

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

def dostuff():
    stuff

def dootherstuff():
    otherstuff

def doevenmorestuff():
    evenmorestuff

actions = ((regex1, dostuff), (regex2, dootherstuff), (regex3, doevenmorestuff))

for regex, action in actions:
    m = re.match(regex, line)
    if m: 
        action()
        break
4 голосов
/ 04 мая 2011
for patt in (regex1, regex2, regex3):
    match = patt.match(line)
    if match:
        if patt == regex1:
            # some handling
        elif patt == regex2:
            # more
        elif patt == regex3:
            # more
        break

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

3 голосов
/ 28 мая 2011

В данном конкретном случае, кажется, нет удобного способа сделать это в Python.если python примет синтаксис:

if (m = re.match(pattern,string)):
    text = m.group(1)

, тогда все будет хорошо, но, очевидно, вы не можете сделать это

3 голосов
/ 04 мая 2011

Прежде всего, вам действительно нужно использовать регулярные выражения для вашего соответствия? Там, где я использовал бы регулярные выражения, например, в perl, я часто буду использовать строковые функции в python (find, setswith и т. Д.).

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

например.,

def search(pattern, s, store):
    match = re.search(pattern, s)
    store.match = match
    return match is not None

class MatchStore(object):
    pass   # irrelevant, any object with a 'match' attr would do

where = MatchStore()
if search(pattern1, s, where):
    pattern1 matched, matchobj in where.match
elif search(pattern2, s, where):
    pattern2 matched, matchobj in where.match
...
1 голос
/ 02 мая 2017

Ваше последнее предложение немного более Pythonic, когда обернуто в функцию:

def parse_line():
    m = re.match(regex1, line)
    if m:
        do stuff
        return
    m = re.match(regex2, line)
    if m:
        do other stuff
        return
    ...

Тем не менее, вы можете приблизиться к тому, что вы хотите, используя простой контейнерный класс с некоторым классом перегрузки операторов:

class ValueCache():
    """A simple container with a returning assignment operator."""
    def __init__(self, value=None):
        self.value = value
    def __repr__(self):
        return "ValueCache({})".format(self.value)
    def set(self, value):
        self.value = value
        return value
    def __call__(self):
        return self.value
    def __lshift__(self, value):
        return self.set(value)
    def __rrshift__(self, value):
        return self.set(value)

match = ValueCache()
if (match << re.match(regex1, line)):
    do stuff with match()
elif (match << re.match(regex2, line)):
    do other stuff with match()
0 голосов
/ 28 декабря 2018

Вы можете определить класс, обертывающий объект сопоставления методом вызова для выполнения сопоставления:

class ReMatcher(object):
    match = None

    def __call__(self, pattern, string):
        self.match = re.match(pattern, string)
        return self.match

    def __getattr__(self, name):
        return getattr(self.match, name)

Затем вызовите его в ваших условиях и используйте, как если бы он был объектом совпадения в полученных блоках:

match = ReMatcher()

if match(regex1, line):
    print(match.group(1))

elif match(regex2, line):
    print(match.group(1))

Это должно работать почти во всех версиях Python, с небольшими изменениями в версиях перед классами нового стиля. Как и в моем другом ответе, вы должны использовать re.compile, если вы беспокоитесь о производительности регулярных выражений.

0 голосов
/ 28 декабря 2018

Вы можете определить локальную функцию, которая принимает регулярное выражение, проверяет его на соответствие вашим входным данным и сохраняет результат в переменной замыкания:

match = None

def matches(pattern):
    nonlocal match, line
    match = re.match(pattern, line)
    return match

if matches(regex1):
    # do stuff with `match`

elif matches(regex2):
    # do other stuff with `match`

Я не уверен, насколько Pythonic такой подход, но я обнаружил, что это самый чистый способ сопоставления регулярных выражений в цепочке if-elif-else и сохранения объектов сопоставления.

Обратите внимание, что этот подход будет работать только в Python 3.0+, так как для него требуется PEP 3104 nonlocal выписка.В более ранних версиях Python не было четкого способа присвоения функции переменной в неглобальной родительской области .

Стоит также отметить, что если у вас достаточно большой файл, то выВы беспокоитесь о том, чтобы дважды выполнять регулярное выражение для каждой строки, вам также следует предварительно скомпилировать их с помощью re.compile и передать полученный объект регулярного выражения в функцию проверки вместо необработанной строки.

0 голосов
/ 10 января 2014

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

0 голосов
/ 05 мая 2011

Мое решение с примером;выполняется только один re.search():

text = '''\
koala + image @ wolf - snow
Good evening, ladies and gentlemen
An uninteresting line
There were 152 ravens on a branch
sea mountain sun ocean ice hot desert river'''

import re
regx3 = re.compile('hot[ \t]+([^ ]+)')
regx2 = re.compile('(\d+|ev.+?ng)')
regx1 = re.compile('([%~#`\@+=\d]+)')
regx  = re.compile('|'.join((regx3.pattern,regx2.pattern,regx1.pattern)))

def one_func(line):
    print 'I am one_func on : '+line

def other_func(line):
    print 'I am other_func on : '+line

def another_func(line):
    print 'I am another_func on : '+line

tupl_funcs = (one_func, other_func, another_func) 


for line in text.splitlines():
    print line
    m = regx.search(line)
    if m:
        print 'm.groups() : ',m.groups()
        group_number = (i for i,m in enumerate(m.groups()) if m).next()
        print "group_number : ",group_number
        tupl_funcs[group_number](line)
    else:
        print 'No match'
        print 'No treatment'
    print

результат

koala + image @ wolf - snow
m.groups() :  (None, None, '+')
group_number :  2
I am another_func on : koala + image @ wolf - snow

Good evening, ladies and gentlemen
m.groups() :  (None, 'evening', None)
group_number :  1
I am other_func on : Good evening, ladies and gentlemen

An uninteresting line
No match
No treatment

There were 152 ravens on a branch
m.groups() :  (None, '152', None)
group_number :  1
I am other_func on : There were 152 ravens on a branch

sea mountain sun ocean ice hot desert river
m.groups() :  ('desert', None, None)
group_number :  0
I am one_func on : sea mountain sun ocean ice hot desert river
0 голосов
/ 04 мая 2011

FWIW, я подчеркивал одно и то же, и я обычно соглашался на 2-й класс (вложенный else s) или какой-то вариант.Я не думаю, что вы найдете что-то намного лучше в целом, если вы хотите оптимизировать читабельность (многие из этих ответов кажутся значительно менее читабельными, чем ваши кандидаты на меня).

Иногда, если выво внешнем цикле или короткой функции вы можете использовать вариант вашей 3-й формы (с break операторами), где вы либо continue или return, и это достаточно читабельно, но я определенно не буду создаватьwhile True блок, чтобы избежать «уродства» других кандидатов.

...