【问题标题】:Parse nested list from string that cannot be parsed with ast.literal_eval从无法用 ast.literal_eval 解析的字符串中解析嵌套列表
【发布时间】:2018-08-23 11:25:00
【问题描述】:

我将一个文件解析为一个 Python 列表,我遇到了一个像这样的嵌套列表:

{   1   4{  2a  0.0 }{  3   0.0 }{  4c  0.0 }{  5   0.0 }   }

我想将它解释为一个列表,但是是嵌套的,所以我希望得到如下的 Python 列表:

[1,4,[2a,0.0],[3,0.0],[4c,0.0],[5,0.0]]

我设法用以下方法做一个正确的字符串:

l = """{    1   4{  2   0.0 }{  3   0.0 }{  4   0.0 }{  5   0.0 }   }"""
l = l.replace("{\t",",[").replace("\t}","]").replace("{","[").replace("}","]").replace("\t",",")[1:]

我也可以申请l.strip("\t"),这样它是一个列表,但不是嵌套的,否则它会被展平,这是我不想要的。

我尝试使用 ast.literal_eval(l),但它在字符串上失败,例如2a

【问题讨论】:

  • 我在这里很困惑。有一个2a 变为2a,但有一个4cc 变为4,但在您发布的正确字符串上,但24 没有任何字符串。正确的输出是什么?
  • 对不起,打错字了,4c 还是 4c,2a 还是 2a,一般都是字符串,即不加 '''' 就不能用 ast 解析
  • 只需按 { 拆分,然后按空格嵌套拆分
  • @E.Serra 你是怎么做到这种按空格嵌套分割的?迭代?
  • 要么使用下面的答案,要么使用第一个列表的 for 循环。另外,我建议您删除列表中的空字符串,如果您有多个空白空间,则会生成这些空字符串

标签: python list parsing abstract-syntax-tree


【解决方案1】:

Pyparsing 有一个内置的帮助器 nestedExpr 来帮助解析开始和结束分隔符之间的嵌套列表:

>>> import pyparsing as pp
>>> nested_braces = pp.nestedExpr('{', '}')
>>> t = """{   1   4{  2a  0.0 }{  3   0.0 }{  4c  0.0 }{  5   0.0 }   }"""
>>> print(nested_braces.parseString(t).asList())
[['1', '4', ['2a', '0.0'], ['3', '0.0'], ['4c', '0.0'], ['5', '0.0']]]

【讨论】:

    【解决方案2】:

    您可以使用 RegEx 开发自己的解析器。在你的情况下,这并不太难。您可以解析封闭的大括号,然后拆分项目并递归评估每个项目。

    这是一个例子(并不完美):

    import re
    
    RE_BRACE = r"\{.*\}"
    RE_ITEM = r"\d+[a-z]+"
    RE_FLOAT = r"[-+]?\d*\.\d+"
    RE_INT = r"\d+"
    
    find_all_items = re.compile(
        "|".join([RE_BRACE, RE_ITEM, RE_FLOAT, RE_INT]),
        flags=re.DOTALL).findall
    
    def parse(text):
        mo = re.match(RE_BRACE, text, flags=re.DOTALL)
        if mo:
            content = mo.group()[1:-1]
            items = [parse(part) for part in find_all_items(content)]
            return items
        mo = re.match(RE_ITEM, text, flags=re.DOTALL)
        if mo:
            return mo.group()
        mo = re.match(RE_FLOAT, text, flags=re.DOTALL)
        if mo:
            return float(mo.group())
        mo = re.match(RE_INT, text, flags=re.DOTALL)
        if mo:
            return int(mo.group())
        raise Exception("Invalid text: {0}".format(text))
    

    注意:此解析器无法正确解析 {1 {2} {3} 4}。为此,您需要像 pyparsing 这样的递归解析器。

    演示:

    s = '''{   1   4{  2a  0.0 }{  3   0.0 }{  4c  0.0 }{  5   0.0 }   }'''
    
    l = parse(s)
    print(l)
    

    你得到:

    [1, 4, ['2a', 0.0, [3, 0.0, '4c', 0.0], 5, 0.0]]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-24
      • 2018-05-14
      相关资源
      最近更新 更多