ValueError:使用 ast.literal_eval 时字符串格式错误 [英] ValueError: malformed string when using ast.literal_eval

查看:106
本文介绍了ValueError:使用 ast.literal_eval 时字符串格式错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

众所周知,使用 eval() 存在潜在的安全风险,因此使用 ast.literal_eval(node_or_string) 被提升

但是在 python 2.7 中,它在运行这个例子时返回 ValueError: malformed string:

<预><代码>>>>ast.literal_eval("4 + 9")

而在 python 3.3 中,此示例按预期工作:

<预><代码>>>>ast.literal_eval('4+9')13

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

解决方案

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

在 Python 3 中进行了更改,以便它支持任何类型的有效数字表达式要在加法和减法的两边.但是,literal_eval 的使用仍然仅限于加法和减法.

这主要是因为 literal_eval 应该是一个将单个 constant 文字(表示为字符串)转换为 Python 对象的函数.有点像简单内置类型的向后 repr.不包括实际的表达式计算,而且它适用于 Python 3 的事实只是其实现的一个很好的副作用.

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

像这样:

import ast,操作符binOps = {ast.Add:operator.add,ast.Sub: operator.sub,ast.Mult: operator.mul,ast.div: operator.div,ast.Mod:operator.mod}def算术评估(s):node = ast.parse(s, mode='eval')def_eval(节点):如果 isinstance(node, ast.Expression):返回 _eval(node.body)elif isinstance(节点,ast.Str):返回节点.selif isinstance(node, ast.Num):返回节点.nelif isinstance(节点,ast.BinOp):返回 binOps[type(node.op)](_eval(node.left), _eval(node.right))别的:raise Exception('不支持的类型{}'.format(node))返回 _eval(node.body)

如您所见,此实现非常简单.当然,它还不支持更复杂的东西,比如取幂和一些一元节点,但添加它并不难.它工作得很好:

<预><代码>>>>算术评估('4+2')6>>>算术评估('4*1+2*​​6/3')8

您甚至可以稍后引入更复杂的东西(例如对诸如 sin() 之类的东西的函数调用).

It is widely known that using eval() is a potential security risk so the use of ast.literal_eval(node_or_string) is promoted

However In python 2.7 it returns ValueError: malformed string when running this example:

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

Whereas in python 3.3 this example works as expected:

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

Why does it run on python 3 and not python 2? How can I fix it in python 2.7 without using the risky eval() function?

解决方案

The reason this doesn’t work on Python 2 lies in its implementation of literal_eval. The original implementation only performed number evaluation for additions and subtractions when the righth operand was a complex number. This is syntactically necessary for complex numbers to be expressed as a literal.

This was changed in Python 3 so that it supports any kind of valid number expression to be on either side of the addition and subtraction. However, the use of literal_eval is still restricted to additions and subtractions.

This is mostly because literal_eval is supposed to be a function that turns a single constant literal (expressed as a string) into a Python object. Kind of like a backwards repr for simple built-in types. Actual expression evaluation is not included, and the fact that this works with Python 3 is just a nice-to-have side effect from its implementation.

In order to evaluate actual expressions, without having to use eval (which we don’t want to), we can write our own expression evaluation algorithm that operates on the AST. This is pretty simple, especially for simple arithmetic operations on numbers (for example to build your own calculator etc.). We simply parse the string into an AST and then evaluate the resulting tree by looking at the different node types and applying the correct operation.

Something like this:

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)

As you can see, this implementation is pretty straightforward. Of course it does not support more complex stuff like exponentiation and some unary nodes yet, but it’s not too difficult to add that. And it works just fine:

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

You could even introduce more complex things later (for example function calls for things like sin()).

这篇关于ValueError:使用 ast.literal_eval 时字符串格式错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆