【问题标题】:Python regex matching in conditionals条件中的 Python 正则表达式匹配
【发布时间】:2011-08-18 19:04:54
【问题描述】:

我正在解析文件,我想根据一些复杂的正则表达式检查每一行。像这样的

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
    ...

这看起来很奇怪。

这样做的正确方法是什么?

【问题讨论】:

标签: python regex conditional


【解决方案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

【讨论】:

  • 更明确地说,你可以像这样使用元组提取for regex, action in actions:
  • 这不太适合以动作为实例方法的方式使用。有解决办法吗?
【解决方案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

我喜欢 Tim 的回答,因为它分离了每个正则表达式匹配的代码以保持简单。对于我的回答,我不会为每场比赛添加超过一两行代码,如果您需要更多,请调用单独的方法。

【讨论】:

    【解决方案3】:

    在这种特殊情况下,python 似乎没有方便的方法来执行此操作。 如果 python 会接受语法:

    if (m = re.match(pattern,string)):
        text = m.group(1)
    

    那么一切都会好起来的,但显然你 不能这样做

    【讨论】:

    • 这将是 Python 3.8 和 PEP 572 的情况,但使用 := 而不是 =
    • 或三元text = m.group(1) if (m := re.match(pattern, string)) else ""
    【解决方案4】:

    首先,您真的需要使用正则表达式进行匹配吗?在我会使用正则表达式的地方,例如 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
    ...
    

    【讨论】:

      【解决方案5】:

      您的最后一个建议在封装在函数中时更符合 Python 风格:

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

      【讨论】:

        【解决方案6】:

        您可以定义一个接受正则表达式的本地函数,根据您的输入对其进行测试,并将结果存储到闭包范围的变量中:

        match = None
        
        def matches(pattern):
            nonlocal match, line
            match = re.match(pattern, line)
            return match
        
        if matches(regex1):
            # do stuff with `match`
        
        elif matches(regex2):
            # do other stuff with `match`
        

        我不确定这种方法如何 Pythonic,但它是我发现在 if-elif-else 链中进行正则表达式匹配并保留匹配对象的最简洁方法。

        请注意,此方法仅适用于 Python 3.0+,因为它需要 PEP 3104 nonlocal 语句。在早期的 Python 版本中 there's no clean way for a function to assign to a variable in a non-global parent scope.

        还值得注意的是,如果您有一个足够大的文件,您担心为每行运行两次正则表达式,您还应该使用 re.compile 预编译它们并将生成的正则表达式对象传递给您的检查函数而不是原始字符串。

        【讨论】:

          【解决方案7】:

          我会将您的正则表达式分解成更小的组件,然后先搜索简单的,然后再搜索更长的匹配项。

          类似:

          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
          

          【讨论】:

            【解决方案8】:

            为什么不使用字典/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)
            

            【讨论】:

            • 我一开始也考虑过使用字典,但是顺序不会被保留,在他的代码示例中,顺序是相关的。
            • 如果你在 python3 中,你可以使用 collections.OrderedDict 或在早期的 python 中 pypi.python.org/pypi/ordereddict
            【解决方案9】:

            FWIW,我曾强调过同样的事情,我通常会选择第二种形式(嵌套 elses)或一些变化形式。如果您希望优化可读性,我认为您通常不会发现任何更好的东西(这些答案中的许多在我看来似乎比您的候选人的可读性要低得多)。

            有时,如果您处于外循环或短函数中,则可以使用第三种形式的变体(带有break 语句的形式),其中continuereturn,这足够可读,但我绝对不会为了避免其他候选人的“丑陋”而创建while True 块。

            【讨论】:

              【解决方案10】:

              我的解决方案举个例子;只有一个 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
              

              【讨论】:

                【解决方案11】:

                创建一个以匹配为状态的类。在条件之前实例化它,这也应该存储您要匹配的字符串。

                【讨论】:

                  【解决方案12】:

                  你可以定义一个类包装匹配对象,调用方法来执行匹配:

                  class ReMatcher(object):
                      match = None
                  
                      def __call__(self, pattern, string):
                          self.match = re.match(pattern, string)
                          return self.match
                  
                      def __getattr__(self, name):
                          return getattr(self.match, name)
                  

                  然后在您的条件中调用它,并在结果块中将其用作匹配对象:

                  match = ReMatcher()
                  
                  if match(regex1, line):
                      print(match.group(1))
                  
                  elif match(regex2, line):
                      print(match.group(1))
                  

                  这几乎适用于任何 Python 版本,在新样式类之前的版本中略有调整。与我的其他答案一样,如果您担心正则表达式的性能,您应该使用 re.compile

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多