Возьмите первый успешный матч из серии регулярных выражений - PullRequest
3 голосов
/ 04 сентября 2010

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

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 ]

6 голосов
/ 04 сентября 2010

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

for reg in regexes:
    m = reg.match(name)
    if m: break
else:
    print 'ARGL NOTHING MATCHES THIS!!!'
2 голосов
/ 04 сентября 2010

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

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

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

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

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

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

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

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

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

1 голос
/ 04 сентября 2010

В 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, является встроенным nextin, который позволяет указать значение по умолчанию, если итератор исчерпан.Если вам нужно смоделировать его в версии 2.5 или более ранней,

def next(itr, deft):
  try: return itr.next()
  except StopIteration: return deft
1 голос
/ 04 сентября 2010

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

m = compiled_regex_1.match(name) or
    compiled_regex_2.match(name) or
    compiled_regex_3.match(name) or
    print("ARGHHHH!")
0 голосов
/ 04 сентября 2010

Эрик находится в лучшем положении, чтобы получить более полное представление о том, что ОП нацеливает, хотя я бы использовал, если бы еще.Я также думаю, что использование функции печати в выражении or мало сомнительно.+1 для Натона исправления 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!!!')
0 голосов
/ 04 сентября 2010

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

regexps = {
  'first': r'...',
  'second': r'...',
}

compiled = re.compile('|'.join('(?P<%s>%s)' % item for item in regexps.iteritems()))
match = compiled.match(my_string)
print match.lastgroup
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...