使用正则表达式在Python中解析数学字符串表达式

时间:2016-03-24 10:01:13

标签: python regex python-3.x

我使用AST构建了一个表达式求值程序,现在想将它连接到解析器。我想使用正则表达式自己构建解析,而不依赖于Pyparser或tokenize库等工具。

有效符号:实数,+, - ,*,/,(,) 数字格式:带或不带小数的任何有效实数,前面带有可选的正/负号。

    num_pattern = r"[-+]?[0-9]*\.?[0-9]+"
    symbol_pattern = "[/+-/*()]"

输入是一个包含数字或符号的表达式,后跟可选空格,后跟数字或符号并重复。

我想找到每个标记,剥离任何空格,添加一个空格,然后重新组合输出(然后我将变成一个以空格分隔的列表)。

我最大的两个问题是负号( - )既可以作为数字的一部分也可以作为运算符出现。我不确定如何分开这两个案例。我也很难将结果分组在一起,让我可以替换它们。我可以找到字符串中的所有数字,我可以找到所有的符号,但我不知道如何创建一个单一的regEx,它将每个多位数字和每个符号分成一组,我这可以替换/添加空间。我知道这与使用贪婪的比赛有关,但我已经迷失了。

示例:

(-23 + 8) * 5   -->   ( -23 + 8 ) * 5
-23--23         -->   -23 - -23
.3 * (-3+ 9)    -->   .3 * ( -3 + 9 )

5 个答案:

答案 0 :(得分:1)

考虑到-23.3等数字,我提出了以下解决方案,该问题在三种情况下使用alteration

正则表达式: ((-?(?:\d+(?:\.\d+)?))|([-+\/*()])|(-?\.\d+))

<强>解释

  • ((-?(?:\d+(?:\.\d+)?))匹配带小数部分的任何带符号的负数或正数。

  • ([-+\/*()])匹配任何令牌。

  • (-?\.\d+))匹配带有小数部分的带符号数字。与.3一样。

替换为:替换为 \1 ,即First captured group space

<强> Regex101 Demo

答案 1 :(得分:1)

虽然你可能会对正则表达式走得很远,但你会遇到不可避免的障碍。正则表达式代码只能解析Chomsky类型2语法(也称为正则表达式或无上下文语法),而数学公式是类型3,它是超集。特别是嵌套的表达式会带来麻烦。

所以你可以使用正则表达式进行扫描,但之后你需要一个具有一种堆栈机制的解析器。

答案 2 :(得分:0)

我更喜欢命名组:

((?P<brackets>[()])|(?P<number>\-?\d*\.?\d+)|(?P<operator>[+\-\*\/]))

参见示例https://regex101.com/r/dW4hP0/2

然后在匹配时使用re.finditer和groupdict。

答案 3 :(得分:0)

noob想出了这个表达的很大一部分。但它并不是按照我想要的方式工作。我做了一系列小调整来实现这个目标:

带有替换((((^|\s)-)?(\d*\.\d+|\d+))|([-+\/*()]))

\1 (注意空格)

主要有两个部分。第一个是数字部分。它将匹配隐式正数和显式负数。它还会处理a.b.b0.b的缩写)形式的十进制数字,但会拒绝右侧没有数字的.

(((^|\s)-)?(\d*\.\d+|\d+))

((^|\s)-)?部分处理可选的负号。为了使否定与减法分开,有一个约束,即负数​​要么以空格为前缀,要么是在字符串的开头。

第二部分([-+\/*()]))只匹配列表中的单个运算符。

示例:

-3              -->     -3
-3+3            -->     -3 + 3
3-3             -->     3 - 3
3--3            -->     3 - - 3 (user error)
3- -3           -->     3 - -3
2+(-3+5)        -->     2 + ( - 3 + 5 ) (user error)
2 + ( -3 + 5)   -->     2 + ( -3 + 5 )
-0.3            -->     -0.3
0.3+.5          -->     0.3 + .5
-.3             -->     -.3
-.3-0.5         -->     -.3 - 0.5

答案 4 :(得分:-1)

目前还不清楚你想做什么。

如果您尝试使用正则表达式解析数学表达式,那么您运气不好,正则表达式不够强大(实际上,您甚至不能将括号与正则表达式匹配:见here

如果您尝试将运算符-与数学-区分开来,那么您也运气不好:要做到这一点,您需要在大多数情况下进行语义分析而您不能这样做&#39;直列&#39;用正则表达式。

最后,您的数字模式中存在错误(最终?必须包含数字部分,否则它将无法按预期工作):

num_pattern = r"[-+]?[0-9]*(\.[0-9]+)?"