使用元组字符串表示时 ast.literal_eval() 引发的格式错误值错误

56 投票
5 回答
174673 浏览
提问于 2025-04-17 14:13

我正在尝试从一个文件中读取一个元组的字符串表示,并将这个元组添加到一个列表里。以下是相关的代码。

raw_data = userfile.read().split('\n')
for a in raw_data : 
    print a
    btc_history.append(ast.literal_eval(a))

这是输出结果:

(Decimal('11.66985'), Decimal('0E-8'))
Traceback (most recent call last):


File "./goxnotify.py", line 74, in <module>
    main()
  File "./goxnotify.py", line 68, in main
    local.load_user_file(username,btc_history)
  File "/home/unix-dude/Code/GoxNotify/local_functions.py", line 53, in load_user_file
    btc_history.append(ast.literal_eval(a))
  File "/usr/lib/python2.7/ast.py", line 80, in literal_eval
    return _convert(node_or_string)

  `File "/usr/lib/python2.7/ast.py", line 58, in _convert
   return tuple(map(_convert, node.elts))
  File "/usr/lib/python2.7/ast.py", line 79, in _convert
   raise ValueError('malformed string')
   ValueError: malformed string

5 个回答

7

在我的情况下,我是这样解决的:

my_string = my_string.replace(':false', ':False').replace(':true', ':True')
ast.literal_eval(my_string)
34

来自文档关于ast.literal_eval()的说明:

这个函数可以安全地评估一个表达式节点或者一个包含Python表达式的字符串。你提供的字符串或节点只能包含以下几种Python基本数据结构:字符串、数字、元组、列表、字典、布尔值和None。

Decimal不在ast.literal_eval()允许的列表中。

49

ast.literal_eval(在ast.py文件里)首先用ast.parse把代码解析成一个树状结构,然后用一个比较复杂的递归函数来评估这些代码,逐个理解树中的元素,并把它们替换成它们的字面意思。不过,这段代码不太好扩展,如果想要在里面加上Decimal,你就得把所有的代码都复制一遍,然后重新开始。

如果想要简单一点的方法,可以使用ast.parse模块来解析表达式,然后用ast.NodeVisitorast.NodeTransformer来确保没有不想要的语法或变量访问。接着用compileeval来得到结果。

这段代码和literal_eval有点不同,因为它实际上使用了eval,但我觉得这样更容易理解,而且不需要深入研究AST树。它特别只允许一些特定的语法,比如明确禁止使用lambda表达式、属性访问(像foo.__dict__这样的访问是非常危险的),或者访问任何不被认为安全的名称。它能很好地解析你的表达式,另外我还添加了Num(浮点数和整数)、列表和字典的字面量。

而且在Python 2.7和3.3上都能正常工作。

import ast
import decimal

source = "(Decimal('11.66985'), Decimal('1e-8'),"\
    "(1,), (1,2,3), 1.2, [1,2,3], {1:2})"

tree = ast.parse(source, mode='eval')

# using the NodeTransformer, you can also modify the nodes in the tree,
# however in this example NodeVisitor could do as we are raising exceptions
# only.
class Transformer(ast.NodeTransformer):
    ALLOWED_NAMES = set(['Decimal', 'None', 'False', 'True'])
    ALLOWED_NODE_TYPES = set([
        'Expression', # a top node for an expression
        'Tuple',      # makes a tuple
        'Call',       # a function call (hint, Decimal())
        'Name',       # an identifier...
        'Load',       # loads a value of a variable with given identifier
        'Str',        # a string literal

        'Num',        # allow numbers too
        'List',       # and list literals
        'Dict',       # and dicts...
    ])

    def visit_Name(self, node):
        if not node.id in self.ALLOWED_NAMES:
            raise RuntimeError("Name access to %s is not allowed" % node.id)

        # traverse to child nodes
        return self.generic_visit(node)

    def generic_visit(self, node):
        nodetype = type(node).__name__
        if nodetype not in self.ALLOWED_NODE_TYPES:
            raise RuntimeError("Invalid expression: %s not allowed" % nodetype)

        return ast.NodeTransformer.generic_visit(self, node)


transformer = Transformer()

# raises RuntimeError on invalid code
transformer.visit(tree)

# compile the ast into a code object
clause = compile(tree, '<AST>', 'eval')

# make the globals contain only the Decimal class,
# and eval the compiled object
result = eval(clause, dict(Decimal=decimal.Decimal))

print(result)

撰写回答