2010-09-04 3 views
3

Я пытаюсь извлечь набор данных из строки, которая может соответствовать одному из трех шаблонов. У меня есть список скомпилированных регулярных выражений. Я хочу пропустить их (по порядку) и пойти с первым матчем.Сделать первый успешный матч из серии регулярных выражений

regexes = [ 
    compiled_regex_1, 
    compiled_regex_2, 
    compiled_regex_3, 
] 

m = None 
for reg in regexes: 
    m = reg.match(name) 
    if m: break 

if not m: 
    print 'ARGL NOTHING MATCHES THIS!!!' 

Это должно работать (еще не проверено), но оно довольно изящно. Есть ли лучший способ кипятить цикл, который ломается, когда он преуспеет или взрывается, когда он этого не делает?

Может быть что-то конкретное для re, о котором я не знаю, что позволяет вам тестировать несколько шаблонов.

ответ

6

Вы можете использовать пункт else петли for:

for reg in regexes: 
    m = reg.match(name) 
    if m: break 
else: 
    print 'ARGL NOTHING MATCHES THIS!!!' 
+1

+1 для правильной, но я получил впечатление, что для-другого конструкция считается запутанным, и, несмотря на множество случаев, когда это именно то, что вы хочу, чтобы это казалось неодобрительным (но я бы хотел, чтобы его опровергли). – msw

+0

Не знал этого. Хотя мои глаза всегда ассоциируют 'else' с' try'; он улавливает меня с помощью 'try' ...' except'. –

+0

Я узнал о 'for..else' как минимум три раза сейчас ... И я все время забываю об этом. Это просто нехорошее именование, но оно отлично работает. Благодарю. – Oli

1

Поскольку у вас есть конечное множество в этом случае, вы могли бы использовать short ciruit evaluation:

m = compiled_regex_1.match(name) or 
    compiled_regex_2.match(name) or 
    compiled_regex_3.match(name) or 
    print("ARGHHHH!") 
2

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

if any(reg.match(name) for reg in regexes): 
    .... 

Однако это не скажет вам, какое регулярное выражение соответствует.

В качестве альтернативы вы можете объединить несколько шаблонов в одном регулярном выражении с |:

regex = re.compile(r"(regex1)|(regex2)|...") 

Опять же, это не покажет вам, какие REGEX совпадают, но вы будете иметь объект соответствия, который можно использовать для получения дополнительной информации. Например, вы можете выяснить, какой из регулярных выражений удалось из группы, которая не является None:

>>> match = re.match("(a)|(b)|(c)|(d)", "c") 
>>> match.groups() 
(None, None, 'c', None) 

Однако это может усложниться, однако, если какой-либо из суб-регулярных выражений есть группы в них, а также, так как нумерация будет быть изменен.

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

0

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

+0

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

+0

Правильно, потому что я использовал dict. Если вместо этого вы используете список кортежей или сортируете regexps.items в этом понимании списка, тогда он четко определен. –

0

Эрик находится в лучшем виде, делая больший снимок того, что ОП нацеливает, я бы использовал, если бы остался. Я также думаю, что с помощью функции печати в or выражения немного сомнительно. +1 для Nathon исправления OP для использования правильного оператора else.

Тогда моя альтернатива:

# alternative to any builtin that returns useful result, 
# the first considered True value 
def first(seq): 
    for item in seq: 
     if item: return item 

regexes = [ 
    compiled_regex_1, 
    compiled_regex_2, 
    compiled_regex_3, 
] 

m = first(reg.match(name) for reg in regexes) 
print(m if m else 'ARGL NOTHING MATCHES THIS!!!') 
1

В Python 2.6 или лучше:

import itertools as it 

m = next(it.ifilter(None, (r.match(name) for r in regexes)), None) 

ifilter вызов может быть сделано в genexp, но только немного неловко, т.е., С обычным трюком для связывания имен в genexp (так называемый «фантома вложенными for дизъюнкцию идиомы»):

m = next((m for r in regexes for m in (r.match(name),) if m), None) 

но itertools обычно предпочтительно, когда это применимо.

Нужный 2.6 - это встроенный next, который позволяет указать значение по умолчанию, если итератор исчерпан. Если у вас есть, чтобы имитировать его в 2,5 или выше,

def next(itr, deft): 
    try: return itr.next() 
    except StopIteration: return deft 
Смежные вопросы