从无法用 ast.literal_eval 解析的字符串解析嵌套列表
Parse nested list from string that cannot be parsed with ast.literal_eval
我将一个文件解析为一个 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
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']]]
您可以使用 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]]
我将一个文件解析为一个 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
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']]]
您可以使用 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]]