Соответствие регулярного выражения Python в условных выражениях
Я разбираю файл, и я хочу проверить каждую строку на несколько сложных регулярных выражений. Что-то вроде этого
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
...
который выглядит просто странно.
Каков правильный способ сделать это?
Ответы
Ответ 1
Вы можете определить функцию для действия, требуемого каждым регулярным выражением, и сделать что-то вроде
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
Ответ 2
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
Во-первых, вам действительно нужно использовать регулярные выражения для соответствия? Где я буду использовать регулярные выражения, например, perl, я часто буду использовать строковые функции в python (find, startswith и т.д.).
Если вам действительно нужно использовать регулярные выражения, вы можете создать простую функцию поиска, которая выполняет поиск, и если совпадение возвращается, задает объект хранилища, чтобы сохранить совпадение перед возвратом 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
...
Ответ 4
В этом конкретном случае, похоже, нет удобного способа сделать это в python.
если python примет синтаксис:
if (m = re.match(pattern,string)):
text = m.group(1)
тогда все будет хорошо, но, видимо, вы
не может этого сделать
Ответ 5
Я бы разложил ваше регулярное выражение на более мелкие компоненты и сначала попробовал простейшие с более длинными совпадениями.
что-то вроде:
if re.match(simplepart,line):
if re.match(complexregex, line):
do stuff
elif re.match(othersimple, line):
if re.match(complexother, line):
do other stuff
Ответ 6
Почему бы не использовать оператор dictionnary/switch?
def action1(stuff):
do the stuff 1
def action2(stuff):
do the stuff 2
regex_action_dict = {regex1 : action1, regex2 : action2}
for regex, action in regex_action_dict.iteritems():
match_object = re.match(regex, line):
if match_object:
action(match_object, line)
Ответ 7
FWIW, я подчеркнул одно и то же, и я обычно соглашаюсь на вторую форму (вложенные else
s) или некоторые варианты. Я не думаю, что вы найдете что-то лучше в целом, если вы хотите оптимизировать читаемость (многие из этих ответов кажутся значительно менее читаемыми, чем ваши кандидаты).
Иногда, если вы находитесь во внешнем цикле или короткой функции, вы можете использовать вариацию своей третьей формы (с инструкциями break
), где вы либо continue
, либо return
, и это достаточно читаемо, но я определенно не создал бы блок while True
, чтобы избежать "уродства" других кандидатов.
Ответ 8
Мое решение с примером; выполняется только один 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
Ответ 9
Сделайте класс с совпадением как состояние. Проигнорируйте его до условного, это должно сохранить строку, которую вы также сопоставляете.
Ответ 10
Ваше последнее предложение немного больше 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()