与Perl不同,据我所知,您无法匹配Python中if语句内的正则表达式,并且无法同时将结果分配给变量。这导致了这样的典型构造:


到目前为止,Python仍然如此。但是,如果我要遍历文件/行数组,检查每行中是否有几个正则表达式,并在没有匹配的情况下触发全部捕获,该怎么办?我想不出我如何绕过一个相当笨拙且深度嵌套的if-else-if-else ...-构造: / pre>

没有任何方法可以将其放置在看起来更漂亮的elif-construction中吗?以下内容在Python中不起作用,因为if子句仅接受表达式,而不接受语句。但是,遵循这些原则的东西会让我感到像是pythonic,还是我对这里显而易见的东西视而不见?? >

#1 楼

只需使用continue关键字强制执行该操作即可评估下一个字符串。
if语句之后的代码仅在前一个语句为false时才执行。

import re
strings = ["abc zzz", "y", "#comment"]
for s in strings:
    match = re.search("(\S+) (\S+)", s)
    if match:
        print "Multiword: %s+%s" % (match.group(1), match.group(2))
        continue
    match = re.match("y$", s)
    if match:
        print "Positive"
        continue
    match = re.match("n$", s)
    if match:
        print "Negative"
        continue

    # a few more matches possible in real life script,
    # and then the last catch-all:
    print "No match found, line skipped"


评论


\ $ \ begingroup \ $
一方面,这对我来说看起来不是很“干净”,另一方面,它很容易理解,我会说它易于维护。明天我将对此进行更深入的检查,但感谢您的答复!
\ $ \ endgroup \ $
–雷神
2014年3月24日15:11

\ $ \ begingroup \ $
@Thor这种结构的唯一问题(我有点喜欢)是,忘记在以后添加的新子句中添加继续非常容易
\ $ \ endgroup \ $
– o0'。
2014年3月24日15:48

#2 楼

为什么不使用元组(re, lambda match: action)的列表,它类似于

actions = [("(\S+) (\S+)", lambda match: "Multiword: %s+%s" % (match.group(1), match.group(2))),
           ("y$", lambda match: "Positive"),
           ("n$", lambda match: "Negative")]


然后:如果需要混合搜索和匹配,则可以使用元组列表:

for rex, action in actions:
     match = re.match(rex, s)
     if match: 
          print action(match)


,如

(matchmethod, rex, action)


当然:

actions = [
    (re.search, "(\S+) (\S+)", lambda match: "Multiword: %s+%s"%(match.group(1), match.group(2)) ),
    (re.match, "y$", lambda match: "Positive"),
    (re.match, "n$", lambda match: "Negative")]


评论


\ $ \ begingroup \ $
错过了以下事实:一个调用是重新搜索,另一个调用是重新匹配。
\ $ \ endgroup \ $
– RemcoGerlich
2014年3月24日15:04

\ $ \ begingroup \ $
@RemcoGerlich:该解决方案可以轻松调整。看到我的编辑。
\ $ \ endgroup \ $
–hivert
2014年3月24日15:08

\ $ \ begingroup \ $
我知道,这是有效的方法。我认为这取决于测试的数量以及它们的代码共有多少,才能确定这是否是一个好主意。总的来说,我认为这会使代码的可读性降低,但有可能使其代码更短,这很好。
\ $ \ endgroup \ $
– RemcoGerlich
2014年3月24日15:10

\ $ \ begingroup \ $
这比我习惯的理解要复杂得多,但是看起来很pythonic。我将对此进行试验,谢谢!
\ $ \ endgroup \ $
–雷神
2014年3月24日15:15

\ $ \ begingroup \ $
@thor:这只是进一步推动Python的switch语句
\ $ \ endgroup \ $
–hivert
2014年3月24日15:20

#3 楼

当找到匹配项时,我将其放在函数中,并从中将其放入return中,这样一来,您就没有else:情况的所有缩进,只有它们的测试和返回列表: >
import re
strings = ["abc zzz", "y", "#comment"]

def run_tests(s)
    match = re.search("(\S+) (\S+)", s)
    if match:
        print "Multiword: %s+%s" % (match.group(1), match.group(2))
        return

    if re.match("y$", s):
        print "Positive"
        return

    if re.match("n$", s):
        print "Negative"
        return

    # a few more matches possible in real life script,
    # and then the last catch-all:
    print "No match found, line skipped"

for s in strings:
    run_tests(s)


我尝试将测试列表放入某些数据结构中以进行循环(例如消息和要测试的模式),但是由于代码略有不同(搜索与匹配) ,简单的打印与匹配的操作相比)更清晰,只是保持这种效果。

评论


\ $ \ begingroup \ $
这很好,直到我想在不同的匹配项下填充不同的变量-然后我总是必须在此函数和调用者之间来回传递可能变量的完整列表,对吗?
\ $ \ endgroup \ $
–雷神
2014年3月24日15:14

\ $ \ begingroup \ $
您总是可以使您的代码任意复杂,是的:-)。在这种情况下,也许所有变量都应该属于某种State类,并且状态变量可以传递给一组test_函数,或者它们可以是该类的方法,等等。
\ $ \ endgroup \ $
– RemcoGerlich
2014年3月24日17:30

#4 楼

我喜欢@hivert的方法,但会对其进行更多形式化:


评论


\ $ \ begingroup \ $
第一个是re.search,其他的是re.match
\ $ \ endgroup \ $
–伊兹卡塔
2014年3月25日19:52

\ $ \ begingroup \ $
@Izkata:这就是为什么我给他们加上“ ^”的原因-它迫使.search的行为类似于.match(仅在行首匹配)。
\ $ \ endgroup \ $
–休·博特威尔
2014年3月25日20:01

#5 楼

这是正则表达式的已知问题。但是您可以将选项放在列表等容器中,然后使用for循环:

import re
strings = ["abc zzz", "y", "#comment"]
regexps_and_messages = [
    ("(\S+) (\S+)", "Multiword: %s+%s"),
    ("y$", "Positive"),
    ("n$", "Negative"),
]

for s in strings:
    for regexp, message in regexps_and_messages:
        m = re.match(regexp, s)
        if m is not None:
            print message % m.groups()
            break
    else: # if no break in above loop
        print "No match found, line skipped"


评论


\ $ \ begingroup \ $
并不是我特别需要re.match与re.search的区别,但这使得无法保留它。另外,以我的经验,for ... else构造很少使用,以至于大多数维护者都不知道它是做什么的?
\ $ \ endgroup \ $
–雷神
2014年3月24日15:13

\ $ \ begingroup \ $
@Thor并非如此,如果您确实需要match的行为,则只需在适用的正则表达式前加上^,例如“ ^ y $”和“ ^ n $”。使用多行正则表达式标志时,其行为与match略有不同(它也将在每个换行符之后匹配,而不是仅在搜索字符串的开头匹配),但是无论如何您都要逐行检查不会有所作为(在这种情况下,$也会在任何换行符之前匹配,因此除非您希望这种行为,否则您也必须担心换行符)。
\ $ \ endgroup \ $
– JAB
2014年3月24日17:07



#6 楼

为了进一步说明建议将正则表达式放在列表中的方法,可以将|和|一起加入正则表达式。然后一次性将线与所有可能的样式进行匹配。

import re

class LineMatcher(object):
    def __init__(self, patterns):
        # In order to match each string, we build a regex combining which can match
        # all the parts. 
        # Something like: ((\S+) (\S+))|(y$)|(n$)|(.*))
        # When we match against it, we can figure out which pattern was matched
        self._groups = {}
        regexes = []

        # because groups could contain groups, we need to keep track of the current
        # group index so that we know which index each pattern will end up with.
        current_group_index = 1
        for pattern, handler in patterns:
            group_count = re.compile(pattern).groups
            self._groups[current_group_index] = (group_count, handler)
            regexes.append("(%s)" % pattern)
            current_group_index += group_count + 1

        self._regex = re.compile("|".join(regexes))

    def match(self, string):
        match = self._regex.match(string)
        group_count, handler = self._groups[match.lastindex]
        captures = match.groups()[match.lastindex:match.lastindex + group_count]
        return handler(*captures)


matcher = LineMatcher([
    ("(\S+) (\S+)", lambda first, second: "Multiword: %s+%s"),
    ("y$", lambda: "Positive"),
    ("n$", lambda: "Negative"),
    (".*", lambda: "No match found, line skipped")
])


strings = ["abc zzz", "y", "#comment"]
for s in strings:
    print matcher.match(s)


评论


\ $ \ begingroup \ $
我喜欢这种解决方案,因为它避免了为每种可能性重新执行正则表达式搜索的过程。从本质上讲,您仍然会有类似数量的条件,但是支票是短路
\ $ \ endgroup \ $
– JAB
2014年3月24日17:14

\ $ \ begingroup \ $
我喜欢这种解决方案,它似乎比@Calpratt的继续方法更具Python风格,可扩展性和“简洁性”。另一方面,我可以在代码的第一眼中就掌握了这种方法,而在这里,我必须自己努力。我想我现在的问题是:如果您偶然发现了必须维护的代码,这会使您的生活更轻松吗?考虑到这一点,我将选择较粗略但更容易的继续方法。
\ $ \ endgroup \ $
–雷神
2014年3月25日上午10:54

\ $ \ begingroup \ $
应该指出,这种方法仅适用于正则表达式是互斥的。例如,如果您需要将其设置为多字并以'y'结尾,则不能使用它。
\ $ \ endgroup \ $
–加布
2014年3月25日14:21

\ $ \ begingroup \ $
@Gabe,实际上在我的代码上面并不互斥。不是。*匹配任何东西。我认为在含糊不清的情况下,它总是返回第一个匹配项。
\ $ \ endgroup \ $
–温斯顿·埃韦特(Winston Ewert)
2014年3月25日20:37

#7 楼

没有continue的Calpratt答案:

import re
strings = ["abc zzz", "y", "#comment"]
for s in strings:
    match = re.search("(\S+) (\S+)", s)
    if match:
        print "Multiword: %s+%s" % (match.group(1), match.group(2))
    elif re.match("y$", s):
        print "Positive"
    elif re.match("n$", s):
        print "Negative"
    else:
        print "No match found, line skipped"


评论


\ $ \ begingroup \ $
是的,但这仅适用于这种情况。从第一个Elif开始,您将没有机会访问比赛结果。甚至存在尝试访问elif分支中的match对象的危险,这将导致处理错误的结果。不能低估,因为这本身不是错,但是它不像其他方法那样灵活。
\ $ \ endgroup \ $
–雷神
2014年3月25日上午10:49

#8 楼

一旦在代码中识别出模式,就可以将其包装为基类以避免样板。这也使代码更易于维护。

import re

class Handler:
    PATTERN = ''
    def __init__(self):
        self._pattern = re.compile(self.PATTERN)

    def match(self, *args, **kwargs):
        return self._pattern.match(*args, **kwargs)

    def handle(self, matched):
        pass

class MultiwordHandler(Handler):
    PATTERN = '(\S+) (\S+)'

    def match(self, *args, **kwargs):
        return self._pattern.search(*args, **kwargs)

    def handle(self, matched):
        print 'Multiword: %s+%s' % (matched.group(1), matched.group(2))

class PositiveHandler(Handler):
    PATTERN = 'y$'

    def handle(self, matched):
        print 'Positive'

class NegativeHandler(Handler):
    PATTERN = 'n$'

    def handle(self, matched):
        print 'Negative'


像这样使用它们:

handlers = [MultiwordHandler(), PositiveHandler(), NegativeHandler()]

strings = ["abc zzz", "y", "#comment"]

for s in strings:
    for handler in handlers:
        matched = handler.match(s)
        if matched:
            handler.handle(matched)
            break
    else:
        print "No match found, line skipped"


#9 楼

您可以通过失败时的方法通过return -ing或使用Python way执行Exceptions来使应用程序流程更加线性,例如:

try:
    if not do_action_1:
        raise Exception('Action 1 failed')
    if not do_action_2:
        raise Exception('Action 2 failed')
    if not do_action_3:
        raise Exception('Action 3 failed')
    if not do_action_4:
        raise Exception('Action 4 failed')
    if not do_action_5:
        raise Exception('Action 5 failed')
except Exception as e:
    print 'Whoops: %s' % e


评论


\ $ \ begingroup \ $
我宁愿称其为异常的滥用。
\ $ \ endgroup \ $
– RemcoGerlich
2014年3月24日14:58在

\ $ \ begingroup \ $
除了觉得必须同意@RemcoGerlich之外,我还必须承认我不太了解如何将其应用于我的情况。 do_action_x-parts是否都应该是单个功能,我要检查的每个匹配项都一个?
\ $ \ endgroup \ $
–雷神
2014年3月24日15:01