是否有适用于Python的数学表达式解析器+计算器?
我不是第一个提出这个问题的人,但是答案通常指向eval()
。例如,可以这样做:
>>> safe_list = ['math','acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', 'degrees', 'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log', 'log10', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'abs']
>>> safe_dict = dict([ (k, locals().get(k, None)) for k in safe_list ])
>>> s = "2+3"
>>> eval(s, {"__builtins__":None}, safe_dict)
5
但这并不安全:
>>> s_badbaduser = """
... (lambda fc=(
... lambda n: [
... c for c in
... ().__class__.__bases__[0].__subclasses__()
... if c.__name__ == n
... ][0]
... ):
... fc("function")(
... fc("code")(
... 0,0,0,0,"KABOOM",(),(),(),"","",0,""
... ),{}
... )()
... )()
... """
>>> eval(s_badbaduser, {"__builtins__":None}, safe_dict)
Segmentation fault
另外,eval
用于解析和评估数学表达式对我来说似乎是错误的。
我找到了PyMathParser,但它也可以在后台使用eval
,并且没有更好的效果:
>>> import MathParser
>>> m=MathParser.PyMathParser()
>>> m.expression = s_badbaduser
>>> m.evaluate();
Segmentation fault
是否有可用的库可以在不使用Python解析器的情况下解析和评估数学表达式?
查看Paul
McGuire的pyparsing
。他既编写了通用解析器又编写了用于算术表达式的语法:
from __future__ import division
import pyparsing as pyp
import math
import operator
class NumericStringParser(object):
'''
Most of this code comes from the fourFn.py pyparsing example
http://pyparsing.wikispaces.com/file/view/fourFn.py
http://pyparsing.wikispaces.com/message/view/home/15549426
__author__='Paul McGuire'
All I've done is rewrap Paul McGuire's fourFn.py as a class, so I can use it
more easily in other places.
'''
def pushFirst(self, strg, loc, toks ):
self.exprStack.append( toks[0] )
def pushUMinus(self, strg, loc, toks ):
if toks and toks[0] == '-':
self.exprStack.append( 'unary -' )
def __init__(self):
"""
expop :: '^'
multop :: '*' | '/'
addop :: '+' | '-'
integer :: ['+' | '-'] '0'..'9'+
atom :: PI | E | real | fn '(' expr ')' | '(' expr ')'
factor :: atom [ expop factor ]*
term :: factor [ multop factor ]*
expr :: term [ addop term ]*
"""
point = pyp.Literal( "." )
e = pyp.CaselessLiteral( "E" )
fnumber = pyp.Combine( pyp.Word( "+-"+pyp.nums, pyp.nums ) +
pyp.Optional( point + pyp.Optional( pyp.Word( pyp.nums ) ) ) +
pyp.Optional( e + pyp.Word( "+-"+pyp.nums, pyp.nums ) ) )
ident = pyp.Word(pyp.alphas, pyp.alphas+pyp.nums+"_$")
plus = pyp.Literal( "+" )
minus = pyp.Literal( "-" )
mult = pyp.Literal( "*" )
div = pyp.Literal( "/" )
lpar = pyp.Literal( "(" ).suppress()
rpar = pyp.Literal( ")" ).suppress()
addop = plus | minus
multop = mult | div
expop = pyp.Literal( "^" )
pi = pyp.CaselessLiteral( "PI" )
expr = pyp.Forward()
atom = ((pyp.Optional(pyp.oneOf("- +")) +
(pi|e|fnumber|ident+lpar+expr+rpar).setParseAction(self.pushFirst))
| pyp.Optional(pyp.oneOf("- +")) + pyp.Group(lpar+expr+rpar)
).setParseAction(self.pushUMinus)
# by defining exponentiation as "atom [ ^ factor ]..." instead of
# "atom [ ^ atom ]...", we get right-to-left exponents, instead of left-to-right
# that is, 2^3^2 = 2^(3^2), not (2^3)^2.
factor = pyp.Forward()
factor << atom + pyp.ZeroOrMore( ( expop + factor ).setParseAction(
self.pushFirst ) )
term = factor + pyp.ZeroOrMore( ( multop + factor ).setParseAction(
self.pushFirst ) )
expr << term + pyp.ZeroOrMore( ( addop + term ).setParseAction( self.pushFirst ) )
self.bnf = expr
# map operator symbols to corresponding arithmetic operations
epsilon = 1e-12
self.opn = { "+" : operator.add,
"-" : operator.sub,
"*" : operator.mul,
"/" : operator.truediv,
"^" : operator.pow }
self.fn = { "sin" : math.sin,
"cos" : math.cos,
"tan" : math.tan,
"abs" : abs,
"trunc" : lambda a: int(a),
"round" : round,
# For Python3 compatibility, cmp replaced by ((a > 0) - (a < 0)). See
# https://docs.python.org/3.0/whatsnew/3.0.html#ordering-comparisons
"sgn" : lambda a: abs(a)>epsilon and ((a > 0) - (a < 0)) or 0}
self.exprStack = []
def evaluateStack(self, s ):
op = s.pop()
if op == 'unary -':
return -self.evaluateStack( s )
if op in "+-*/^":
op2 = self.evaluateStack( s )
op1 = self.evaluateStack( s )
return self.opn[op]( op1, op2 )
elif op == "PI":
return math.pi # 3.1415926535
elif op == "E":
return math.e # 2.718281828
elif op in self.fn:
return self.fn[op]( self.evaluateStack( s ) )
elif op[0].isalpha():
return 0
else:
return float( op )
def eval(self, num_string, parseAll = True):
self.exprStack = []
results = self.bnf.parseString(num_string, parseAll)
val = self.evaluateStack( self.exprStack[:] )
return val
nsp = NumericStringParser()
print(nsp.eval('1+2'))
# 3.0
print(nsp.eval('2*3-5'))
# 1.0
问题内容: 我有一个网站,用户可以在其中输入数学方程式(表达式),然后根据该网站提供的数据(常数)对这些方程式进行评估。所需的数学运算,包括符号,算术运算,以及其他一些基本功能。一个示例方程式可以是: 可以使用Python来简单地做到这一点,但是众所周知,这会导致站点受损。做数学方程式评估的安全方法是什么? 如果选择使用Python本身来评估表达式,那么将存在任何限制Python的Python沙箱
问题内容: 如何允许用户以安全的方式执行数学表达式?我需要写一个完整的解析器吗? 是否有类似ast.literal_eval()的东西,但用于表达式? 问题答案: “ Pyparsing示例”页面列出了几个表达式解析器: http://pyparsing.wikispaces.com/file/view/fourFn.py-使用pyparsing的常规算术中缀表示法解析器/评估器实现(尽管它的名称
问题内容: 您好,我经常开发JTableModels,其中某些单元格必须包含将某个简单数学公式加总的结果。该公式可以具有: 运算符(+,-,*,/) 数字常数 其他单元格引用(包含数字) 参数(引用名称如“ INTEREST_RATE”的数字) 我经常通过制作一个小的计算器类来解决该问题,该类可以解析公式,定义的语法。计算器类使用堆栈进行计算,语法始终使用波兰语表示法。 但是波兰语对我和我的用户来
问题内容: 我怎样才能(轻松地)获取一个字符串,例如用户可能在运行时输入的字符串,并产生一个Python函数,该函数可以被评估为任何值? 问题答案: 如果您使用Python表示法,则Python自己的内部编译器可以对此进行解析。 如果您稍微改变一下表示法,就会更快乐。 您将获得一个可以使用的抽象语法树。
系列专栏: 安卓系统Framework面经专栏链接:Android系统面试题解析大全 安卓系统Framework面经目录详情:Android系统面经_Framework开发面经_150道面试题答案解析 安卓高频面经解析大全专栏链接:Android面试题解析大全 安卓高频面经解析大全目录详情:安卓面经_Android面经_150道安卓基础面试题目录 嵌入式面经解析大全专栏链接:嵌入式面经_C++软件
JCA提供者提出的解决方案是将JAR放在jre/lib/ext文件夹中,但不是从那里加载的。据我所知,这是由于OSGi(Eclipse equinox)类加载器策略将bootstrap类加载器作为每个bundle类加载器的父类,从而排除了从jre/lib/ext文件夹加载的扩展类加载器。即。在jre/lib/ext文件夹中没有一个bundle可以看到任何东西。 是否有一种方法可以让Eclipse