为什么表达式的顺序在 re.match 中很重要?

Why does the order of expressions matter in re.match?

我正在制作一个函数,它将接受“three()”或“{1 + 2}”之类的字符串并将它们放入标记列表中(例如:“three()”= [ "three", "(", ")"] 我使用 re.match 来帮助分隔字符串。

def lex(s):
# scan input string and return a list of its tokens
seq = []
patterns = (r"^(\t|\n|\r| )*(([a-z])*|[0-9]|\(|\)|\*|\/|)(\t|\n|\r| )*")
m = re.match(patterns,s)
while m != None:
    if s == '':
        break
    seq.append(m.group(2))
    s = s[len(m.group(0)):]
    m = re.match(patterns,s)
return seq

如果字符串只是“三”,则此方法有效。但如果字符串包含“()”或任何符号,它就会留在 while 循环中。 但是当在它起作用的模式字符串中移动 ([a-z])* 时会发生一件有趣的事情。为什么会这样?

works: patterns = (r"^(\t|\n|\r| )*([0-9]|\(|\)|\*|\/|([a-z])*)(\t|\n|\r| )*")
Does not work: patterns = (r"^(\t|\n|\r| )*(([a-z])*|[0-9]|\(|\)|\*|\/)(\t|\n|\r| )*")

这个有点棘手,但问题出在这部分([a-z])*。这匹配任何大小为 0(零)或更大的小写字母字符串。

如果你把这个序列放在最后,就像这里:

patterns = (r"^(\t|\n|\r| )*([0-9]|\(|\)|\*|\/|([a-z])*)(\t|\n|\r| )*")

正则表达式引擎将首先尝试其他匹配项,如果找到匹配项,就停止。只有当 none 的其他人匹配时,它才会尝试 ([a-z])* 并且由于 * 是 'greedy',它将匹配所有 three,然后继续匹配 ( 最后是 ).

阅读有关如何测试完整表达式的解释 in the documentation(感谢 @kaya3)。

但是,如果您将该序列放在开头,如下所示:

patterns = (r"^(\t|\n|\r| )*(([a-z])*|[0-9]|\(|\)|\*|\/)(\t|\n|\r| )*")

它将首先尝试匹配它。它仍然很贪心,所以 three 仍然匹配。但是在下一次尝试中,它将尝试将 ([a-z])* 与剩余的 '()' 匹配 - 并且它 匹配 ,因为该字符串以零字母开头。

一直这样匹配,一直卡在循环里。您可以通过将 * 更改为 + 来修复它,只有在有 1 个或多个匹配项时才会匹配:

patterns = (r"^(\t|\n|\r| )*(([a-z])+|[0-9]|\(|\)|\*|\/)(\t|\n|\r| )*")