在Python中解析用户提供的数学公式的安全方法

在Python中解析用户提供的数学公式的安全方法,第1张

在Python中解析用户提供的数学公式的安全方法

查看Paul
McGuire的pyparsing
。他既编写了通用解析器又编写了用于算术表达式的语法:

from __future__ import divisionimport pyparsing as pypimport mathimport operatorclass NumericStringParser(object):    '''    Most of this pre 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 valnsp = NumericStringParser()print(nsp.eval('1+2'))# 3.0print(nsp.eval('2*3-5'))# 1.0


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5653086.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存