代码之家  ›  专栏  ›  技术社区  ›  dbr

替代“match=re.match();if match:…”成语?

  •  31
  • dbr  · 技术社区  · 15 年前

    如果要检查是否有与regex匹配的内容,如果是,请打印第一组,您需要……

    import re
    match = re.match("(\d+)g", "123g")
    if match is not None:
        print match.group(1)
    

    这完全是学究式的,但是中间人 match 变量有点烦人。

    像Perl这样的语言通过创建新的 $1 $9 匹配组的变量,如..

    if($blah ~= /(\d+)g/){
        print $1
    }
    

    this reddit comment ,

    with re_context.match('^blah', s) as match:
        if match:
            ...
        else:
            ...
    

    …我认为这是一个有趣的想法,所以我写了一个简单的实现:

    #!/usr/bin/env python2.6
    import re
    
    class SRE_Match_Wrapper:
        def __init__(self, match):
            self.match = match
    
        def __exit__(self, type, value, tb):
            pass
    
        def __enter__(self):
            return self.match
    
        def __getattr__(self, name):
            if name == "__exit__":
                return self.__exit__
            elif name == "__enter__":
                return self.__name__
            else:
                return getattr(self.match, name)
    
    def rematch(pattern, inp):
        matcher = re.compile(pattern)
        x = SRE_Match_Wrapper(matcher.match(inp))
        return x
        return match
    
    if __name__ == '__main__':
        # Example:
        with rematch("(\d+)g", "123g") as m:
            if m:
                print(m.group(1))
    
        with rematch("(\d+)g", "123") as m:
            if m:
                print(m.group(1))
    

    (理论上,此功能可以修补到 _sre.SRE_Match 对象)

    如果你能跳过 with 语句的代码块(如果没有匹配项),这会将此代码块简化为..

    with rematch("(\d+)g", "123") as m:
        print(m.group(1)) # only executed if the match occurred
    

    …但根据我的推断,这似乎是不可能的 PEP 343

    有什么想法吗?如我所说,这真是微不足道的烦恼,几乎到了编码高尔夫的地步。

    9 回复  |  直到 6 年前
        1
  •  12
  •   Glenn Maynard    15 年前

    我认为这不是小事。如果我经常这样编写代码,我不想在代码周围撒上多余的条件。

    这有点奇怪,但您可以使用迭代器来实现:

    import re
    
    def rematch(pattern, inp):
        matcher = re.compile(pattern)
        matches = matcher.match(inp)
        if matches:
            yield matches
    
    if __name__ == '__main__':
        for m in rematch("(\d+)g", "123g"):
            print(m.group(1))
    

    奇怪的是,它使用了一个迭代器来处理一些不重复的事情——它更接近于一个条件,乍一看,它可能会为每个匹配产生多个结果。

    上下文管理器不能完全跳过它的托管函数,这似乎很奇怪;虽然这不是“with”的明确用例之一,但它似乎是一个自然的扩展。

        2
  •  4
  •   mhubig    12 年前

    另一个好的语法是这样的:

    header = re.compile('(.*?) = (.*?)$')
    footer = re.compile('(.*?): (.*?)$')
    
    if header.match(line) as m:
        key, value = m.group(1,2)
    elif footer.match(line) as m
        key, value = m.group(1,2)
    else:
        key, value = None, None
    
        3
  •  1
  •   AMADANON Inc.    11 年前

    基于格伦·梅纳德的解决方案,我还有另一种方法:

    for match in [m for m in [re.match(pattern,key)] if m]:
        print "It matched: %s" % match
    

    与格伦的解决方案类似,这一方法可以是0次(如果没有匹配)或1次(如果匹配)。

    不需要Sub,结果就不那么整洁了。

        4
  •  0
  •   Glenn Maynard    15 年前

    如果你在一个地方做了很多这样的事情,这里有一个替代的答案:

    import re
    class Matcher(object):
        def __init__(self):
            self.matches = None
        def set(self, matches):
            self.matches = matches
        def __getattr__(self, name):
            return getattr(self.matches, name)
    
    class re2(object):
        def __init__(self, expr):
            self.re = re.compile(expr)
    
        def match(self, matcher, s):
            matches = self.re.match(s)
            matcher.set(matches)
            return matches
    
    pattern = re2("(\d+)g")
    m = Matcher()
    if pattern.match(m, "123g"):
        print(m.group(1))
    if not pattern.match(m, "x123g"):
        print "no match"
    

    您可以使用与re相同的线程安全性编译一次regex,为整个函数创建一个可重用的matcher对象,然后非常简洁地使用它。这样做还有一个好处,那就是您可以用明显的方式反转它——要用迭代器实现这一点,您需要传递一个标志来告诉它反转其结果。

    但是,如果您只对每个函数进行一次匹配,就没有多大帮助了;您不希望将matcher对象保持在比这更广泛的上下文中;它会导致与blixt解决方案相同的问题。

        5
  •  0
  •   Blixt    15 年前

    我不认为用 with 在这种情况下是解决方案。你必须提出一个例外 BLOCK 部件(由用户指定)并具有 __exit__ 方法返回 True “接受”例外。所以看起来永远都不好。

    我建议使用类似于Perl语法的语法。把你自己的扩展 re 模块(我叫它 rex )并在其模块命名空间中设置变量:

    if rex.match('(\d+)g', '123g'):
        print rex._1
    

    正如您在下面的注释中看到的,这个方法既不是范围安全的,也不是线程安全的。只有当您完全确信您的应用程序在将来不会成为多线程的,并且您正在使用的范围中调用的任何函数都将成为多线程的,您才能使用它。 使用相同的方法。

        6
  •  0
  •   etuardu    13 年前

    这看起来不太好看,但你可以从 getattr(object, name[, default]) 像这样使用它的内置函数:

    >>> getattr(re.match("(\d+)g", "123g"), 'group', lambda n:'')(1)
    '123'
    >>> getattr(re.match("(\d+)g", "X23g"), 'group', lambda n:'')(1)
    ''
    

    模仿 如果匹配打印组 流,你可以(ab)使用 for 声明如下:

    >>> for group in filter(None, [getattr(re.match("(\d+)g", "123g"), 'group', None)]):
            print(group(1))
    123
    >>> for group in filter(None, [getattr(re.match("(\d+)g", "X23g"), 'group', None)]):
            print(group(1))
    >>> 
    

    当然,您可以定义一个小函数来完成脏工作:

    >>> matchgroup = lambda p,s: filter(None, [getattr(re.match(p, s), 'group', None)])
    >>> for group in matchgroup("(\d+)g", "123g"):
            print(group(1))
    123
    >>> for group in matchgroup("(\d+)g", "X23g"):
            print(group(1))
    >>> 
    
        7
  •  0
  •   oneself    13 年前

    不是完美的解决方案,但允许您为同一个str链接多个匹配选项:

    class MatchWrapper(object):
      def __init__(self):
        self._matcher = None
    
      def wrap(self, matcher):
        self._matcher = matcher
    
      def __getattr__(self, attr):
        return getattr(self._matcher, attr)
    
    def match(pattern, s, matcher):
      m = re.match(pattern, s)
      if m:
        matcher.wrap(m)
        return True
      else:
        return False
    
    matcher = MatchWrapper()
    s = "123g";
    if _match("(\d+)g", line, matcher):
      print matcher.group(1)
    elif _match("(\w+)g", line, matcher):
      print matcher.group(1)
    else:
      print "no match"
    
        8
  •  0
  •   dennis    10 年前

    我的解决方案是:

    import re
    
    s = 'hello world'
    
    match = []
    if match.append(re.match('w\w+', s)) or any(match):
        print('W:', match.pop().group(0))
    elif match.append(re.match('h\w+', s)) or any(match):
        print('H:', match.pop().group(0))
    else:
        print('No match found')
    

    你可以用多少 否则如果 必要时的条款。

    更好的是:

    import re
    
    s = 'hello world'
    
    if vars().update(match=re.match('w\w+', s)) or match:
        print('W:', match.group(0))
    elif vars().update(match=re.match('h\w+', s)) or match:
        print('H:', match.group(0))
    else:
        print('No match found')
    

    两个 追加 更新 返回 没有 . 因此,您必须使用 在任何情况下都是不可分割的。

    不幸的是,这只在代码位于顶层(即不在函数中)时有效。

        9
  •  0
  •   Rhubbarb    10 年前

    这就是我要做的:

    def re_match_cond (match_ref, regex, text):
        match = regex.match (text)
        del match_ref[:]
        match_ref.append (match)
        return match
    
    if __name__ == '__main__':
        match_ref = []
        if re_match_cond (match_ref, regex_1, text):
            match = match_ref[0]
            ### ...
        elif re_match_cond (match_ref, regex_2, text):
            match = match_ref[0]
            ### ...
        elif re_match_cond (match_ref, regex_3, text):
            match = match_ref[0]
            ### ...
        else:
            ### no match
            ### ...
    

    也就是说,我将一个列表传递给函数以模拟按引用传递。