值错误:使用ast.literal时字符串格式不正确_

2024-04-28 10:15:01 发布

您现在位置:Python中文网/ 问答频道 /正文

众所周知,使用eval()是一种潜在的安全风险,因此推广使用^{}

但是在python 2.7中,当运行以下示例时,它返回ValueError: malformed string

>>> ast.literal_eval("4 + 9")

而在Python3.3中,此示例的工作方式与预期一致:

>>> ast.literal_eval('4+9')
13

为什么它运行在python 3而不是python 2上?如何在python 2.7中修复它而不使用有风险的eval()函数?


Tags: 函数示例stringeval方式ast风险valueerror
3条回答

这在Python 2上不起作用的原因在于它实现了literal_eval。原始实现仅在右操作数为复数时对加法和减法执行数值计算。这在语法上是必要的,复数要表示为一个文字。

这是Python 3中的was changed,因此它支持任何类型的有效数字表达式位于加减运算的任何一边。然而,literal_eval的使用仍然限于加法和减法。

这主要是因为literal_eval应该是一个函数,它将单个常量文本(表示为字符串)转换为Python对象。有点像简单内置类型的向后repr。实际的表达式求值不包括在内,而这与Python3一起工作的事实只是它实现的一个很好的副作用。

为了计算实际表达式,不必使用eval(我们不想这样做),我们可以编写自己的表达式计算算法,该算法在AST上运行。这是非常简单的,特别是对数字进行简单的算术运算(例如构建自己的计算器等)。我们只需将字符串解析为AST,然后通过查看不同的节点类型并应用正确的操作来计算结果树。

像这样的:

import ast, operator

binOps = {
    ast.Add: operator.add,
    ast.Sub: operator.sub,
    ast.Mult: operator.mul,
    ast.Div: operator.div,
    ast.Mod: operator.mod
}

def arithmeticEval (s):
    node = ast.parse(s, mode='eval')

    def _eval(node):
        if isinstance(node, ast.Expression):
            return _eval(node.body)
        elif isinstance(node, ast.Str):
            return node.s
        elif isinstance(node, ast.Num):
            return node.n
        elif isinstance(node, ast.BinOp):
            return binOps[type(node.op)](_eval(node.left), _eval(node.right))
        else:
            raise Exception('Unsupported type {}'.format(node))

    return _eval(node.body)

如您所见,这个实现非常简单。当然,它还不支持更复杂的东西,比如指数运算和一些一元节点,但是添加它并不太困难。而且效果很好:

>>> arithmeticEval('4+2')
6
>>> arithmeticEval('4*1+2*6/3')
8

以后甚至可以引入更复杂的内容(例如,函数调用sin())。

这是为了支持复数(因为issue 4907)。例如,1 + 2j被解析器解析为一个表达式,该表达式由整数文本、加法运算和imaginary literal组成;但是由于complex numbers是一个内置类型,因此ast.literal_eval需要支持复数语法。

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

如果要解析任意算术表达式,应该解析到语法树(使用^{})、verify it with a whitelist,然后计算。

利用消息来源,卢克!

^{}^{}

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

>>> 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这样的数字,它从来没有打算做简单的整数加法。然后在python 3中,他们增加了literal_eval来处理这些情况。

相关问题 更多 >