题 格式错误的字符串ValueError ast.literal_eval(),字符串表示为元组


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

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

28
2018-01-30 18:41


起源


如果它是值得信赖的输入 - 你能评估它吗? - Jon Clements♦
多数民众赞成我最初尝试的,它给了我一个SyntaxError:解析时意外的EOF。这是值得信赖的输入。 - Sinthet
这个 是 恼人的... - Andy Hayden
你有没有跟进为什么它会给你SyntaxError?通常,eval()是没有人会建议您使用的东西,但由于它是可信输入,这将是您做所需的最简单方法。 - hfaran


答案:


ast.literal_eval (位于 ast.py用树解析树 ast.parse 首先,它用一个非常丑陋的递归函数来评估代码,解释解析树元素并用它们的文字等价物替换它们。不幸的是,代码根本不可扩展,所以要添加 Decimal 你需要复制所有代码并重新开始代码。

对于稍微简单的方法,您可以使用 ast.parse 模块解析表达式,然后是 ast.NodeVisitor 要么 ast.NodeTransformer 确保没有不需要的语法或不需要的变量访问。然后编译 compile 和 eval 得到结果。

代码有点不同 literal_eval 因为这段代码实际上是使用的 eval,但在我看来,理解起来更简单,而且不需要深入研究AST树。它特别只允许一些语法,明确禁止例如lambdas,属性访问(foo.__dict__ 非常邪恶),或访问任何不安全的名称。它解析你的表达很好,另外我还补充说 Num (浮点数和整数),列表和字典文字。

此外,在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)

33
2017-08-12 01:25



请注意,在Pythons 3.4+等中,有一个新的节点类型,如None,False和True - Antti Haapala


来自 文件 对于 ast.literal_eval()

安全地评估表达式节点或包含Python表达式的字符串。 提供的字符串或节点可能只包含以下Python文字结构:字符串,数字,元组,列表,dicts,布尔值和None。

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


19
2018-01-30 18:51



Bleh,我希望它最高级别的元组能让它变好。不管怎样,谢谢。 - Sinthet
我找到了一个迂回的方法来解决我的问题,我只是为此付出了一笔赏金,但我想从其他人那里看到更高质量的解决方案,特别是NPE,因为他已经在这个帖子中拥有一些权限。 - jdero
@jdero:如果您遇到不受信任的输入,唯一安全的解决方案是创建一个自定义解析器来处理输入字符串。 - NPE
@NPE自定义解析器的外观如何?如果你用更多的牛肉更新你的答案,我会很高兴接受它。 - jdero
实际上你不需要自定义 解析器,使用Python解析器然后解释AST树就足够了 - Antti Haapala