【问题标题】:Regular expression for simple math expressions简单数学表达式的正则表达式
【发布时间】:2014-03-29 12:13:19
【问题描述】:

作为一个练习,我试图想出一个正则表达式来评估简单的代数,比如

q = '23 * 345 - 123+65'

从这里我想得到'23'、'*'、'345'、'-'、'123'、'+'、'65'。

现在,我搜索了类似的问题,其他人已经解决了这个问题。但我真正想知道的是为什么 my 解决方案不起作用。

这是我得到的最好的:

regexparse = '(\d+\s*(\*|\/|\+|\-)\s*)+(\d+\s*)'

解释

  • (\d+\s*(*|/|+|-)\s*)+
    • ( 一个或多个数字 \d+ 后面可以跟空格 \s* 然后必须跟一个符号 (*|/|+|-) 然后后面可以跟更多空格 \s* 和整个事情必须至少出现一次)+
  • (\d+\s*)
    • 一个或多个数字,后面可以跟空格

但是,当我运行代码时

m = re.match(regexparse, q)
print m.group(0)
print m.group(1)
print m.group(2)
print m.group(3)

我明白了

23 * 345 - 123+65
123+
+
65

所以就像第一个块匹配尽可能少的字符一样。为什么?

【问题讨论】:

    标签: python regex


    【解决方案1】:

    这是你的正则表达式:

    (\d+\s*(\*|\/|\+|\-)\s*)+(\d+\s*)
    

    (\d+\s*(\*|\/|\+|\-)\s*) 将匹配表达式的第一部分:23 * 并将 * 存储在第二组中。

    然后+ 使其重复,但由于重复捕获组仅保留它们的最后一个匹配项,它将丢弃23 * *,而是匹配第二组中的345 - -

    + 在下一次重复时再次工作,以丢弃最后一次捕获,而是在第一组中捕获 123+,在第二组中捕获 +

    接下来,+ 不能再重复,所以它停止,(\d+\s*) 开始匹配以获取 65


    重复捕获组仅存储最后一个捕获的事实是正则表达式的设计方式,并且在所有正则表达式引擎 AFAIK 中都是如此。


    进一步阐述:

    重复匹配和重复捕获是有区别的。尝试:(\d)+ on 12345,您会看到只有 5 会被捕获。就像那样,因为您的父母被分配了一个特定的组捕获。第一组分配给组 1,如果组 1 有很多捕获,则只能保留 1,这是最后一个。这就是正则表达式的工作原理,不幸的是,as per the docs:

    如果一个组匹配多次,则只能访问最后一个匹配


    如果你想得到你想要的输出,你可以使用re.findall并匹配\d+|[+/*-]

    import re
    q = '23 * 345 - 123+65'
    regexparse = r'\d+|[+/*-]'
    elem = re.findall(regexparse, q)
    print(elem)
    #=> ['23', '*', '345', '-', '123', '+', '65']
    

    【讨论】:

    • 和 aliteralmind 说的一样,但是如果 + 只保留最后一个匹配项,为什么 \d+ 保留整个匹配项?另外,但我可以重复并保留整个内容?
    • 好的,作为第一个解释发生了什么并提供了一个解决方案的人,我将把它标记为正确的。但是可以将您的 cmets 复制到答案正文中吗?对于组织。
    • @oneloop 好的,我可以做到。之后我会移除我的 cmets。
    【解决方案2】:

    我只能说一般的正则表达式,因为我不知道python,但你的问题是在

    (\d+\s*[\*/+-]\s*)+(\d+\s*)
    

    这部分

    (\d+\s*[\*/+-]\s*)+
    

    正在重复,当它完全完成评估时,您只会看到最后一个。

    【讨论】:

    • 我知道,但为什么呢?为什么当我有 ( )+ 它是“被重复,但你只看到最后一个”,而当我有 \d+ 它被重复,但我看到了完整的东西?
    • 您的正则表达式重复匹配您想要的部分内容。没有“整体”。如果你想要每场比赛,你已经分解了你的正则表达式并循环通过它的匹配,以拦截它们。通过一次执行您的正则表达式,其中有一个重复组,然后在 之后执行其他操作,您只会看到最后一个。它确实匹配了每一个,但是除了最后一个之外的每一个都被立即丢弃。
    【解决方案3】:

    试试这个。

    import re
    q = '23 * 345 - 123+65'
    regexparse = r'(\d+)|[-+*/]'
    for i in re.finditer(regexparse, q):
        print i.group(0)
    

    输出:

    23
    *
    345
    -
    123
    +
    65
    

    【讨论】:

      【解决方案4】:

      您的正则表达式令人困惑。为此最好使用re.split()

      q = '23 * 345 - 123+65'
      print re.split('\s*([-+/*])\s*', q)
      

      输出:

      ['23', '*', '345', '-', '123', '+', '65']
      

      【讨论】: