【问题标题】:Match groups in PythonPython中的匹配组
【发布时间】:2011-02-02 23:52:31
【问题描述】:

在 Python 中有没有一种方法可以在不显式创建匹配对象的情况下访问匹配组(或另一种美化以下示例的方法)?

下面是一个例子来说明我提出这个问题的动机:

遵循 Perl 代码

if    ($statement =~ /I love (\w+)/) {
  print "He loves $1\n";
}
elsif ($statement =~ /Ich liebe (\w+)/) {
  print "Er liebt $1\n";
}
elsif ($statement =~ /Je t\'aime (\w+)/) {
  print "Il aime $1\n";
}

翻译成Python

m = re.search("I love (\w+)", statement)
if m:
  print "He loves",m.group(1)
else:
  m = re.search("Ich liebe (\w+)", statement)
  if m:
    print "Er liebt",m.group(1)
  else:
    m = re.search("Je t'aime (\w+)", statement)
    if m:
      print "Il aime",m.group(1)

看起来很别扭(if-else-cascade,匹配对象创建)。

【问题讨论】:

  • 警告:Python re.match() 专门匹配目标的开头。因此 re.match("I love (\w+)", "Oh! How I love you") 不匹配。您要么想使用 re.search() 要么使用适当的通配符模式显式地为正则表达式添加前缀 re.match(".* I love (\w+)", ...)
  • @Jim Dennis:感谢指出;我相应地调整了python示例
  • @S.Lott:哎呀,你是对的。我没看到,虽然我在发帖之前一直在寻找;尽管如此,这里还是有有价值的新答案

标签: python regex


【解决方案1】:

您可以创建一个返回调用 match 的布尔结果的小类,并且保留匹配的组以供后续检索:

import re

class REMatcher(object):
    def __init__(self, matchstring):
        self.matchstring = matchstring

    def match(self,regexp):
        self.rematch = re.match(regexp, self.matchstring)
        return bool(self.rematch)

    def group(self,i):
        return self.rematch.group(i)


for statement in ("I love Mary", 
                  "Ich liebe Margot", 
                  "Je t'aime Marie", 
                  "Te amo Maria"):

    m = REMatcher(statement)

    if m.match(r"I love (\w+)"): 
        print "He loves",m.group(1) 

    elif m.match(r"Ich liebe (\w+)"):
        print "Er liebt",m.group(1) 

    elif m.match(r"Je t'aime (\w+)"):
        print "Il aime",m.group(1) 

    else: 
        print "???"

更新 Python 3 print as a function 和 Python 3.8 赋值表达式 - 现在不需要 REmatcher 类:

import re

for statement in ("I love Mary",
                  "Ich liebe Margot",
                  "Je t'aime Marie",
                  "Te amo Maria"):

    if m := re.match(r"I love (\w+)", statement):
        print("He loves", m.group(1))

    elif m := re.match(r"Ich liebe (\w+)", statement):
        print("Er liebt", m.group(1))

    elif m := re.match(r"Je t'aime (\w+)", statement):
        print("Il aime", m.group(1))

    else:
        print()

【讨论】:

  • 它可能很冗长,但您会将 REmatcher 类放在一个不错的模块中,您将在需要时导入该模块。对于以后不会再出现的问题,你不会问这个问题吧?
  • @ΤZΩΤZΙΟΥ:我同意;但是,为什么模块re中还没有这样的类呢?
  • @Curd:因为是你提出来的。数以千计的 Python 代码库的其他提交者在没有它的情况下生活得很好,那么 为什么 在 re 模块中应该有这样的类?无论如何,如果您认为此类功能属于 re 模块,则非常欢迎您提供补丁。否则,请不要问“为什么事情不像我认为的那样?”问题,因为它们是非生产性的。
  • @ΤZΩΤZΙΟΥ:我不同意。满足于“成千上万的其他人”不考虑引入它的事实只是愚蠢的。如果我不问“为什么”,我怎么能确定没有充分的理由不上这样的课程?我没有看到,但也许其他人可以解释它(从而更好地了解 Python 的哲学)。这是一个很好的例子,说明这些问题很有成效:stackoverflow.com/questions/837265/…
  • “为什么”问题通常是有成效的,但您的问题属于“为什么不喜欢”子类别(强调“我喜欢”),这不可能回答。你认为这样的函数/类是最有用的,然后问为什么其他人没有对它采取行动。为了发生改变,有动机的人(这里:你)必须向社区的其他人(这里:Python 社区)证明改变的合理性。向社区询问为什么尚未引入您想要的更改是非常以自我为中心且没有效率的。
【解决方案2】:

效率较低,但看起来更简单:

m0 = re.match("I love (\w+)", statement)
m1 = re.match("Ich liebe (\w+)", statement)
m2 = re.match("Je t'aime (\w+)", statement)
if m0:
  print "He loves",m0.group(1)
elif m1:
  print "Er liebt",m1.group(1)
elif m2:
  print "Il aime",m2.group(1)

Perl 的问题在于隐式更新某些隐藏变量。这在 Python 中很难实现,因为您需要有一个赋值语句来实际更新任何变量。

重复更少(效率更高)的版本是这样的:

pats = [
    ("I love (\w+)", "He Loves {0}" ),
    ("Ich liebe (\w+)", "Er Liebe {0}" ),
    ("Je t'aime (\w+)", "Il aime {0}")
 ]
for p1, p3 in pats:
    m= re.match( p1, statement )
    if m:
        print p3.format( m.group(1) )
        break

一些 Perl 人喜欢的小变化:

pats = {
    "I love (\w+)" : "He Loves {0}",
    "Ich liebe (\w+)" : "Er Liebe {0}",
    "Je t'aime (\w+)" : "Il aime {0}",
}
for p1 in pats:
    m= re.match( p1, statement )
    if m:
        print pats[p1].format( m.group(1) )
        break

这几乎不值得一提,除非它有时来自 Perl 程序员。

【讨论】:

  • @S.Lott:好的,您的解决方案避免了 if-else-cascade,但代价是进行了不必要的匹配(如果 m0 匹配,则不需要 m1 和 m2);这就是为什么我对这个解决方案不太满意。
  • 如果上一个变体中的键顺序很重要,请务必告诉 OP 使用 OrderedDict。
【解决方案3】:

Python 3.8 开始,并引入assignment expressions (PEP 572):= 运算符),我们现在可以在变量中捕获条件值re.search(pattern, statement)(让我们全部使用match),以便同时检查它是否是不是None,然后在条件体中重新使用它:

if match := re.search('I love (\w+)', statement):
  print(f'He loves {match.group(1)}')
elif match := re.search("Ich liebe (\w+)", statement):
  print(f'Er liebt {match.group(1)}')
elif match := re.search("Je t'aime (\w+)", statement):
  print(f'Il aime {match.group(1)}')

【讨论】:

    【解决方案4】:

    这不是正则表达式解决方案。

    alist={"I love ":""He loves"","Je t'aime ":"Il aime","Ich liebe ":"Er liebt"}
    for k in alist.keys():
        if k in statement:
           print alist[k],statement.split(k)[1:]
    

    【讨论】:

      【解决方案5】:

      你可以创建一个辅助函数:

      def re_match_group(pattern, str, out_groups):
          del out_groups[:]
          result = re.match(pattern, str)
          if result:
              out_groups[:len(result.groups())] = result.groups()
          return result
      

      然后像这样使用它:

      groups = []
      if re_match_group("I love (\w+)", statement, groups):
          print "He loves", groups[0]
      elif re_match_group("Ich liebe (\w+)", statement, groups):
          print "Er liebt", groups[0]
      elif re_match_group("Je t'aime (\w+)", statement, groups):
          print "Il aime", groups[0]
      

      它有点笨拙,但它可以完成工作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-02
        • 2020-12-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-03
        相关资源
        最近更新 更多