Как мне искать совпадения регулярных выражений в Python? - PullRequest
5 голосов
/ 08 января 2010

Мне нужно попробовать строку с несколькими (исключительными - это означает, что строка, соответствующая одному из них, не может соответствовать ни одному из других) регулярных выражений, и выполнить другой фрагмент кода в зависимости от того, к какому из них он подходит. Что у меня сейчас есть:

m = firstre.match(str)
if m:
    # Do something

m = secondre.match(str)
if m:
    # Do something else

m = thirdre.match(str)
if m:
    # Do something different from both

Помимо уродства, этот код сопоставляется со всеми регулярными выражениями даже после того, как он соответствует одному из них (скажем, firstre), что неэффективно. Я пытался использовать:

elif m = secondre.match(str)

но узнал, что присваивание недопустимо в операторах if.

Есть ли элегантный способ добиться того, чего я хочу?

Ответы [ 7 ]

4 голосов
/ 08 января 2010
def doit( s ):

    # with some side-effect on a
    a = [] 

    def f1( s, m ):
        a.append( 1 )
        print 'f1', a, s, m

    def f2( s, m ):
        a.append( 2 )
        print 'f2', a, s, m

    def f3( s, m ):
        a.append( 3 )
        print 'f3', a, s, m

    re1 = re.compile( 'one' )
    re2 = re.compile( 'two' )
    re3 = re.compile( 'three' )


    func_re_list = (
        ( f1, re1 ), 
        ( f2, re2 ), 
        ( f3, re3 ),
    )
    for myfunc, myre in func_re_list:
        m = myre.match( s )
        if m:
            myfunc( s, m )
            break


doit( 'one' ) 
doit( 'two' ) 
doit( 'three' ) 
3 голосов
/ 08 января 2010

Это хорошее приложение для недокументированного, но весьма полезного re.Scanner класса.

3 голосов
/ 08 января 2010

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

import re
class MultiRe(object):
    def __init__(self, **regexps):
        self.keys = regexps.keys()
        self.union_re = re.compile("|".join("(?P<%s>%s)" % kv for kv in regexps.items()))

    def match(self, string, *args):
        result = self.union_re.match(string, *args)
        if result:
            for key in self.keys:
                if result.group(key) is not None:
                    return key

Поиск будет выглядеть так:

multi_re = MultiRe(foo='fo+', bar='ba+r', baz='ba+z')
match = multi_re.match('baaz')
if match == 'foo':
     # one thing
elif match == 'bar':
     # some other thing
elif match == 'baz':
     # or this
else:
     # no match
1 голос
/ 08 января 2010

Вы можете использовать

def do_first(str, res, actions):
  for re,action in zip(res, actions):
    m = re.match(str)
    if m:
      action(str)
      return

Так, например, скажем, вы определили

def do_something_1(str):
  print "#1: %s" % str

def do_something_2(str):
  print "#2: %s" % str

def do_something_3(str):
  print "#3: %s" % str

firstre  = re.compile("foo")
secondre = re.compile("bar")
thirdre  = re.compile("baz")

Тогда назовите это с

do_first("baz",
         [firstre,        secondre,       thirdre],
         [do_something_1, do_something_2, do_something_3])
1 голос
/ 08 января 2010

Несколько идей, ни одна из которых не обязательно хорошая, но она может хорошо вписаться в ваш код:

Как насчет помещения кода в отдельную функцию, то есть MatchRegex(), которая возвращает то, с каким регулярным выражением он соответствует Таким образом, внутри функции вы можете использовать return после того, как вы сопоставили первое (или второе) регулярное выражение, что означает потерю неэффективности.

Конечно, вы всегда можете использовать только вложенные if операторы:

m = firstre.match(str)
if m:
   # Do something
else:
    m = secondre.match(str)
    ...

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

0 голосов
/ 08 января 2010

Сделайте это с помощью elif на тот случай, если вам просто нужно соответствие True / False из регулярного выражения:

if regex1.match(str):
    # do stuff
elif regex2.match(str):
    # and so on
0 голосов
/ 08 января 2010

Ранний возврат, возможно?

def doit(s):
    m = re1.match(s)
    if m:
        # Do something
        return

    m = re2.match(s)
    if m:
        # Do something else
        return

    ...

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

re = re.compile(r'''(?x)    # set the verbose flag
    (?P<foo> fo+ )
  | (?P<bar> ba+r )
  | #...other alternatives...
''')

def doit(s):
    m = re.match(s)
    if m.group('foo'):
        # Do something
    elif m.group('bar'):
        # Do something else
    ...

Я сделал это много. Это быстро и работает с re.finditer.

...