【问题标题】:How do I search through regex matches in Python?如何在 Python 中搜索正则表达式匹配项?
【发布时间】:2010-01-08 14:28:21
【问题描述】:

我需要针对多个(排他性 - 意味着匹配其中一个的字符串不能匹配任何其他)正则表达式尝试一个字符串,并根据它匹配的一个执行不同的代码。我目前拥有的是:

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 语句中不允许赋值。

有没有一种优雅的方式来实现我想要的?

【问题讨论】:

    标签: python regex switch-statement


    【解决方案1】:
    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' ) 
    

    【讨论】:

    • +1 表示纯粹的 Python 风格。就个人而言,我会将元组列表放在 for 语句之外,例如match_functions = ((f1,re1),(f2,re2),..)for myfunc,myre in match_functions:
    • 不要忘记添加“break”以保存尝试匹配列表的其余部分。
    • 根据 cmets 的建议和实例编辑。
    • 我终于实现了一个类似这样的解决方案。就我而言,我能够将 4 个案例中的 3 个重构为一个函数。所以我直接单独匹配第一个正则表达式,如果不匹配,则遍历其他 3 个正则表达式,并使用适当的参数调用函数。为了根据正则表达式调用具有不同参数的函数,我制作了一个(正则表达式:(arg1,arg2))的字典。代码(至少恕我直言)比以前更优雅。非常感谢。
    【解决方案2】:

    这可能有点过度设计解决方案,但您可以将它们作为单个正则表达式与命名组组合并查看哪个组匹配。这可以封装为一个辅助类:

    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
    

    【讨论】:

    • 从我的角度来看,这是工程。我觉得代码不太容易理解。
    【解决方案3】:

    对于未记录但非常有用的re.Scanner 类,这是一个很好的应用程序。

    【讨论】:

      【解决方案4】:

      一些想法,不一定是好的,但它可能很适合您的代码:

      如何将代码放在一个单独的函数中,即MatchRegex(),它返回它匹配的正则表达式。这样,在函数内部,您可以在匹配第一个(或第二个)正则表达式后使用 return,这意味着您失去了效率。

      当然,您总是可以只使用嵌套的 if 语句:

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

      我真的没有理由不使用嵌套的ifs。它们很容易理解,而且效率如你所愿。我会选择它们只是因为它们的简单性。

      【讨论】:

      • 如果有几百个正则表达式怎么办?超过 10 岁的代码几乎无法阅读。
      • @kibitzer:在这种情况下,设计一个通用解决方案是有意义的。或者在预计会增长到那个的情况下。不是每次你都要写 3 个嵌套的 if。
      【解决方案5】:

      你可以使用

      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])
      

      【讨论】:

        【解决方案6】:

        或许是提前回归?

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

        Ants Aasma 的回答也不错。如果您喜欢更少的脚手架,您可以使用verbose regex syntax 自己写出来。

        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

        【讨论】:

          【解决方案7】:

          如果您只需要正则表达式匹配的真/假,请使用 elif:

          if regex1.match(str):
              # do stuff
          elif regex2.match(str):
              # and so on
          

          【讨论】:

          • 我认为他需要 regex.match(str) 的返回值
          猜你喜欢
          • 1970-01-01
          • 2012-01-17
          • 2017-01-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-12-11
          • 2013-03-17
          相关资源
          最近更新 更多