【问题标题】:Malformed String ValueError ast.literal_eval() with String representation of Tuple带有元组字符串表示的格式错误的字符串 ValueError ast.literal_eval()
【发布时间】:2013-01-14 16:51:43
【问题描述】:

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

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

【问题讨论】:

  • 如果它是受信任的输入 - 你能评估它吗?
  • 这就是我最初尝试的,它给了我 SyntaxError: unexpected EOF while parsing。这是受信任的输入。
  • 烦人...
  • 你有没有跟进它为什么给你那个 SyntaxError?通常,没有人会推荐您使用 eval(),但由于它是受信任的输入,因此这将是满足您需要的最简单方法。
  • 查看我对类似问题的回答:stackoverflow.com/questions/15197673/…

标签: python parsing python-2.x abstract-syntax-tree representation


【解决方案1】:

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

为了更简单的方法,您可以使用ast.parse 模块来解析表达式,然后使用ast.NodeVisitorast.NodeTransformer 来确保没有不需要的语法或不需要的变量访问。然后用compileeval编译得到结果。

该代码与literal_eval 有点不同,因为该代码实际上使用了eval,但在我看来更容易理解,不需要深入挖掘 AST 树。它专门只允许某些语法,明确禁止例如 lambda、属性访问(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)

【讨论】:

  • 请注意,在 Python 3.4+ 等版本中,有一个新的节点类型,如 None、False 和 True
【解决方案2】:

documentationast.literal_eval()

安全地计算表达式节点或包含 Python 表达式的字符串。 提供的字符串或节点只能由以下 Python 文字结构组成:字符串、数字、元组、列表、字典、布尔值和无。

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

【讨论】:

  • Bleh,我希望它是最高级别的元组这一事实可以使它正常。无论如何,谢谢。
  • 我找到了一个迂回的方法来解决我刚刚付出的赏金,但我希望看到其他人提供更高质量的解决方案,尤其是 NPE,因为他已经在这个线程中拥有一定的权威。跨度>
  • @jdero:如果您面临不可信的输入,唯一安全的解决方案是创建自定义解析器来处理输入字符串。
  • @NPE 自定义解析器是什么样的?如果你用更多的牛肉更新你的答案,我很乐意接受。
  • 其实你不需要自定义的解析器,你使用Python解析器然后解释AST树就足够了
【解决方案3】:

我知道这是一个老问题,但我认为找到了一个非常简单的答案,以防万一有人需要。

如果你在你的字符串中加上引号(“'hello'”),ast_literaleval() 会完全理解它。

你可以使用一个简单的函数:

    def doubleStringify(a):
        b = "\'" + a + "\'"
        return b

或者可能更适合这个例子:

    def perfectEval(anonstring):
        try:
            ev = ast.literal_eval(anonstring)
            return ev
        except ValueError:
            corrected = "\'" + anonstring + "\'"
            ev = ast.literal_eval(corrected)
            return ev

【讨论】:

    【解决方案4】:

    如果输入是可信的(在您的情况下),则使用 eval() 而不是 ast.literal_eval()

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

    这在 Python 3.6.0 中适用于我

    【讨论】:

      【解决方案5】:

      在我的情况下,我解决了:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-12-18
        • 1970-01-01
        • 2019-01-13
        • 2016-09-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多