首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ValueError:使用ast.literal_eval时字符串格式不正确

ValueError:使用ast.literal_eval时字符串格式不正确
EN

Stack Overflow用户
提问于 2013-12-24 01:22:20
回答 4查看 18K关注 0票数 23

众所周知,使用eval()是一种潜在的安全风险,因此推广使用ast.literal_eval(node_or_string)

然而,在Python2.7中,当运行此示例时,它会返回ValueError: malformed string

代码语言:javascript
复制
>>> ast.literal_eval("4 + 9")

而在python 3.3中,此示例的工作方式与预期不谋而合:

代码语言:javascript
复制
>>> ast.literal_eval('4+9')
13

为什么它运行在python 3上,而不是python 2上?如何在不使用危险的eval()函数的情况下在Python2.7中修复它?

EN

回答 4

Stack Overflow用户

发布于 2013-12-24 01:47:59

它是为了支持复数(从issue 4907开始)。例如,解析器将1 + 2j解析为由整数、加法运算和imaginary literal组成的表达式;但由于complex numbers是内置类型,因此ast.literal_eval最好支持复数语法。

2.x和3.x之间的change in behaviour是为了支持以“错误的方式”写入复数,例如1j + 2;它允许任意加法或减法表达式的事实是一个(大多是意想不到的)副作用。

如果要解析任意算术表达式,则应该解析为语法树(使用ast.parse)、verify it with a whitelist,然后进行求值。

票数 6
EN

Stack Overflow用户

发布于 2013-12-24 01:35:43

使用源码,卢克!

http://hg.python.org/cpython/file/2.7/Lib/ast.py#l40 http://hg.python.org/cpython/file/3.2/Lib/ast.py#l39

你会在那里找到你的答案。具体地说,2.7版本对line 70有一个奇怪的限制,即BinOp的右侧节点是复杂的。

代码语言:javascript
复制
>>> sys.version
'2.7.3 (default, Sep 26 2013, 20:03:06) \n[GCC 4.6.3]'
>>> ast.literal_eval('9 + 0j')
(9 + 0j)
>>> ast.literal_eval('0j + 9')
ValueError: malformed string

我猜2.7的目的是允许对复杂的文字进行literal_eval,例如像9 + 0j这样的数字,它从来没有打算做简单的整数加法。然后在Python3中,他们增强了literal_eval来处理这些情况。

票数 3
EN

Stack Overflow用户

发布于 2013-12-24 10:46:14

使用pyparsing拼凑一个简单的表达式求值器并不太难。

假设您要计算以下表达式类型的表达式,包括括号:

代码语言:javascript
复制
2+3
4.0^2+5*(2+3+4)
1.23+4.56-7.890
(1+2+3+4)/5
1e6^2/1e7

这是对SimpleCalc示例的简化:

代码语言:javascript
复制
import pyparsing as pp
import re

ex='''\
2+3
4.0^2+5*(2+3+4)
1.23+4.56-7.890
(1+2+3+4)/5
1e6^2/1e7'''

e = pp.CaselessLiteral('E')
dec, plus, minus, mult, div, expop=map(pp.Literal,'.+-*/^')
addop  = plus | minus
multop = mult | div
lpar, rpar=map(pp.Suppress,'()')
p_m = plus | minus

num = pp.Word(pp.nums) 
integer = pp.Combine( pp.Optional(p_m) + num )
floatnumber = pp.Combine( integer +
                       pp.Optional( dec + pp.Optional(num) ) +
                       pp.Optional( e + integer ) )

stack=[]
def pushFirst(s, l, t):
    stack.append( t[0] )

expr=pp.Forward()
atom = ((floatnumber | integer).setParseAction(pushFirst) | 
         ( lpar + expr.suppress() + rpar )
       )

factor = pp.Forward()
factor << atom + pp.ZeroOrMore( ( expop + factor ).setParseAction( pushFirst ) )

term = factor + pp.ZeroOrMore( ( multop + factor ).setParseAction( pushFirst ) )
expr << term + pp.ZeroOrMore( ( addop + term ).setParseAction( pushFirst ) )    

pattern=expr+pp.StringEnd()

opn = { "+" : ( lambda a,b: a + b ),
        "-" : ( lambda a,b: a - b ),
        "*" : ( lambda a,b: a * b ),
        "/" : ( lambda a,b: a / b ),
        "^" : ( lambda a,b: a ** b ) }

def evaluateStack(stk):
    op = stk.pop()
    if op in "+-*/^":
        op2 = evaluateStack(stk)
        op1 = evaluateStack(stk)
        return opn[op](op1, op2)
    elif re.search('^[-+]?[0-9]+$',op):
        return int(op)
    else:
        return float(op)     

for line in ex.splitlines():
    parse=pattern.parseString(line)   
    s=stack[:]
    print('"{}"->{} = {}'.format(line,s,evaluateStack(stack)))   

打印:

代码语言:javascript
复制
"2+3"->['2', '3', '+'] = 5
"4.0^2+5*(2+3+4)"->['4.0', '2', '^', '5', '2', '3', '+', '4', '+', '*', '+'] = 61.0
"1.23+4.56-7.890"->['1.23', '4.56', '+', '7.890', '-'] = -2.1000000000000005
"(1+2+3+4)/5"->['1', '2', '+', '3', '+', '4', '+', '5', '/'] = 2.0
"1e6^2/1e7"->['1E6', '2', '^', '1E7', '/'] = 100000.0
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20748202

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档