解析嵌套列表并为每个有效列表返回原始字符串
Parsing nested lists and returning original strings for every valid list
假设我有一个字符串s = '{aaaa{bc}xx{d{e}}f}'
,它有一个嵌套列表结构。我希望它有一个分层表示,同时能够访问与有效子列表对应的子字符串。为简单起见,让我们忘记层次结构,我只想要一个与有效子列表相对应的子字符串列表,例如:
['{aaaa{bc}xx{d{e}}f}', '{bc}', '{d{e}}', '{e}']
使用nestedExpr
,可以获得嵌套结构,其中包括所有有效的子列表:
import pyparsing as pp
s = '{aaaa{bc}xx{d{e}}f}'
not_braces = pp.CharsNotIn('{}')
expr = pp.nestedExpr('{', '}', content=not_braces)
res = expr('L0 Contents').parseString(s)
print(res.dump())
打印:
[['aaaa', ['bc'], 'xx', ['d', ['e']], 'f']]
- L0 Contents: [['aaaa', ['bc'], 'xx', ['d', ['e']], 'f']]
[0]:
['aaaa', ['bc'], 'xx', ['d', ['e']], 'f']
[0]:
aaaa
[1]:
['bc']
[2]:
xx
[3]:
['d', ['e']]
[0]:
d
[1]:
['e']
[4]:
f
为了获得已解析元素的原始字符串表示形式,我必须将其包装到 pyparsing.originalTextFor()
中。但是,这将从结果中删除所有子列表:
s = '{aaaa{bc}xx{d{e}}f}'
not_braces = pp.CharsNotIn('{}')
expr = pp.nestedExpr('{', '}', content=not_braces)
res = pp.originalTextFor(expr)('L0 Contents').parseString(s)
print(res.dump())
打印:
['{aaaa{bc}xx{d{e}}f}']
- L0 Contents: '{aaaa{bc}xx{d{e}}f}'
实际上,originalTextFor()
包装器将其中的所有内容展平。
问题。是否有 originalTextFor()
的替代方法来保留其子解析元素的结构? (最好有一个非丢弃的类似物,它可以用于为解析的子表达式创建命名标记)
注意scanString()
只会给我0级子列表,不会看里面。我想,我可以使用 setParseAction()
,但是 ParserElement
的内部操作模式没有文档记录,而且我还没有机会深入研究源代码。谢谢!
更新 1. 有点相关:
不使用 originalTextFor
,而是将 nestedExpr
表达式包装在 locatedExpr
:
中
import pyparsing as pp
parser = pp.locatedExpr(pp.nestedExpr('{','}'))
locatedExpr
将 return 一个 3 元素的 ParseResults:
- 开始位置
- 解析值
- 结束位置
然后您可以将解析操作附加到此解析器以修改已解析的标记,并添加您自己的 original_string
命名结果,其中包含从输入字符串中截取的原始文本:
def extract_original_text(st, loc, tokens):
start, tokens[:], end = tokens[0]
tokens['original_string'] = st[start:end]
parser.addParseAction(extract_original_text)
现在使用此解析器解析并转储结果:
result = parser.parseString(s)
print(result.dump())
打印:
['aaaa', ['bc'], 'xx', ['d', ['e']], 'f']
- original_string: '{aaaa{bc}xx{d{e}}f}'
并使用以下方式访问 original_string
结果:
print(result.original_string)
编辑 - 如何将 original_string 附加到每个嵌套子结构
要在子结构上维护原始字符串,需要做的工作比 nested_expr
中完成的要多一些。您几乎必须实现自己的递归解析器。
要实现您自己的 nested_expr
版本,您将从以下内容开始:
LBRACE, RBRACE = map(pp.Suppress, "{}")
expr = pp.Forward()
term = pp.Word(pp.alphas)
expr_group = pp.Group(LBRACE + expr + RBRACE)
expr_content = term | expr_group
expr <<= expr_content[...]
print(expr.parseString(sample).dump())
这将转出解析结果,不带 'original_string' 名称:
{aaaa{bc}xx{d{e}}f}
[['aaaa', ['bc'], 'xx', ['d', ['e']], 'f']]
[0]:
['aaaa', ['bc'], 'xx', ['d', ['e']], 'f']
[0]:
aaaa
[1]:
['bc']
[2]:
xx
[3]:
['d', ['e']]
[0]:
d
[1]:
['e']
[4]:
f
要添加 'original_string' 名称,我们首先将 Group
更改为 locatedExpr
包装器。
expr_group = pp.locatedExpr(LBRACE + expr + RBRACE)
这将为每个嵌套子组添加开始和结束位置(使用 nestedExpr
时您无法访问)。
{aaaa{bc}xx{d{e}}f}
[[0, 'aaaa', [5, 'bc', 9], 'xx', [11, 'd', [13, 'e', 16], 17], 'f', 19]]
[0]:
[0, 'aaaa', [5, 'bc', 9], 'xx', [11, 'd', [13, 'e', 16], 17], 'f', 19]
- locn_end: 19
- locn_start: 0
- value: ['aaaa', [5, 'bc', 9], 'xx', [11, 'd', [13, 'e', 16], 17], 'f']
[0]:
aaaa
[1]:
[5, 'bc', 9]
- locn_end: 9
- locn_start: 5
- value: ['bc']
...
我们的解析动作现在也更复杂了。
def extract_original_text(st, loc, tokens):
# pop/delete names and list items inserted by locatedExpr
# (save start and end locations to local vars)
tt = tokens[0]
start = tt.pop("locn_start")
end = tt.pop("locn_end")
tt.pop("value")
del tt[0]
del tt[-1]
# add 'original_string' results name
orig_string = st[start:end]
tt['original_string'] = orig_string
expr_group.addParseAction(extract_original_text)
通过此更改,您现在将获得此结构:
{aaaa{bc}xx{d{e}}f}
[['aaaa', ['bc'], 'xx', ['d', ['e']], 'f']]
[0]:
['aaaa', ['bc'], 'xx', ['d', ['e']], 'f']
- original_string: '{aaaa{bc}xx{d{e}}f}'
[0]:
aaaa
[1]:
['bc']
- original_string: '{bc}'
[2]:
xx
[3]:
['d', ['e']]
- original_string: '{d{e}}'
[0]:
d
[1]:
['e']
- original_string: '{e}'
[4]:
f
注意:ParseResults.dump 的当前版本存在一个限制,它只显示键 或 子项,而不是两者 - 此输出需要修复以删除限制,将在下一个 pyparsing 版本中发布。但是即使 dump() 没有显示这些子结构,它们在您的实际结构中也存在,如果您打印出结果的 repr 就可以看到:
print(repr(result[0]))
(['aaaa', (['bc'], {'original_string': '{bc}'}), 'xx', (['d', (['e'], {'original_string': '{e}'})], {'original_string': '{d{e}}'}), 'f'], {'original_string': '{aaaa{bc}xx{d{e}}f}'})
假设我有一个字符串s = '{aaaa{bc}xx{d{e}}f}'
,它有一个嵌套列表结构。我希望它有一个分层表示,同时能够访问与有效子列表对应的子字符串。为简单起见,让我们忘记层次结构,我只想要一个与有效子列表相对应的子字符串列表,例如:
['{aaaa{bc}xx{d{e}}f}', '{bc}', '{d{e}}', '{e}']
使用nestedExpr
,可以获得嵌套结构,其中包括所有有效的子列表:
import pyparsing as pp
s = '{aaaa{bc}xx{d{e}}f}'
not_braces = pp.CharsNotIn('{}')
expr = pp.nestedExpr('{', '}', content=not_braces)
res = expr('L0 Contents').parseString(s)
print(res.dump())
打印:
[['aaaa', ['bc'], 'xx', ['d', ['e']], 'f']]
- L0 Contents: [['aaaa', ['bc'], 'xx', ['d', ['e']], 'f']]
[0]:
['aaaa', ['bc'], 'xx', ['d', ['e']], 'f']
[0]:
aaaa
[1]:
['bc']
[2]:
xx
[3]:
['d', ['e']]
[0]:
d
[1]:
['e']
[4]:
f
为了获得已解析元素的原始字符串表示形式,我必须将其包装到 pyparsing.originalTextFor()
中。但是,这将从结果中删除所有子列表:
s = '{aaaa{bc}xx{d{e}}f}'
not_braces = pp.CharsNotIn('{}')
expr = pp.nestedExpr('{', '}', content=not_braces)
res = pp.originalTextFor(expr)('L0 Contents').parseString(s)
print(res.dump())
打印:
['{aaaa{bc}xx{d{e}}f}']
- L0 Contents: '{aaaa{bc}xx{d{e}}f}'
实际上,originalTextFor()
包装器将其中的所有内容展平。
问题。是否有 originalTextFor()
的替代方法来保留其子解析元素的结构? (最好有一个非丢弃的类似物,它可以用于为解析的子表达式创建命名标记)
注意scanString()
只会给我0级子列表,不会看里面。我想,我可以使用 setParseAction()
,但是 ParserElement
的内部操作模式没有文档记录,而且我还没有机会深入研究源代码。谢谢!
更新 1. 有点相关:
不使用 originalTextFor
,而是将 nestedExpr
表达式包装在 locatedExpr
:
import pyparsing as pp
parser = pp.locatedExpr(pp.nestedExpr('{','}'))
locatedExpr
将 return 一个 3 元素的 ParseResults:
- 开始位置
- 解析值
- 结束位置
然后您可以将解析操作附加到此解析器以修改已解析的标记,并添加您自己的 original_string
命名结果,其中包含从输入字符串中截取的原始文本:
def extract_original_text(st, loc, tokens):
start, tokens[:], end = tokens[0]
tokens['original_string'] = st[start:end]
parser.addParseAction(extract_original_text)
现在使用此解析器解析并转储结果:
result = parser.parseString(s)
print(result.dump())
打印:
['aaaa', ['bc'], 'xx', ['d', ['e']], 'f']
- original_string: '{aaaa{bc}xx{d{e}}f}'
并使用以下方式访问 original_string
结果:
print(result.original_string)
编辑 - 如何将 original_string 附加到每个嵌套子结构
要在子结构上维护原始字符串,需要做的工作比 nested_expr
中完成的要多一些。您几乎必须实现自己的递归解析器。
要实现您自己的 nested_expr
版本,您将从以下内容开始:
LBRACE, RBRACE = map(pp.Suppress, "{}")
expr = pp.Forward()
term = pp.Word(pp.alphas)
expr_group = pp.Group(LBRACE + expr + RBRACE)
expr_content = term | expr_group
expr <<= expr_content[...]
print(expr.parseString(sample).dump())
这将转出解析结果,不带 'original_string' 名称:
{aaaa{bc}xx{d{e}}f}
[['aaaa', ['bc'], 'xx', ['d', ['e']], 'f']]
[0]:
['aaaa', ['bc'], 'xx', ['d', ['e']], 'f']
[0]:
aaaa
[1]:
['bc']
[2]:
xx
[3]:
['d', ['e']]
[0]:
d
[1]:
['e']
[4]:
f
要添加 'original_string' 名称,我们首先将 Group
更改为 locatedExpr
包装器。
expr_group = pp.locatedExpr(LBRACE + expr + RBRACE)
这将为每个嵌套子组添加开始和结束位置(使用 nestedExpr
时您无法访问)。
{aaaa{bc}xx{d{e}}f}
[[0, 'aaaa', [5, 'bc', 9], 'xx', [11, 'd', [13, 'e', 16], 17], 'f', 19]]
[0]:
[0, 'aaaa', [5, 'bc', 9], 'xx', [11, 'd', [13, 'e', 16], 17], 'f', 19]
- locn_end: 19
- locn_start: 0
- value: ['aaaa', [5, 'bc', 9], 'xx', [11, 'd', [13, 'e', 16], 17], 'f']
[0]:
aaaa
[1]:
[5, 'bc', 9]
- locn_end: 9
- locn_start: 5
- value: ['bc']
...
我们的解析动作现在也更复杂了。
def extract_original_text(st, loc, tokens):
# pop/delete names and list items inserted by locatedExpr
# (save start and end locations to local vars)
tt = tokens[0]
start = tt.pop("locn_start")
end = tt.pop("locn_end")
tt.pop("value")
del tt[0]
del tt[-1]
# add 'original_string' results name
orig_string = st[start:end]
tt['original_string'] = orig_string
expr_group.addParseAction(extract_original_text)
通过此更改,您现在将获得此结构:
{aaaa{bc}xx{d{e}}f}
[['aaaa', ['bc'], 'xx', ['d', ['e']], 'f']]
[0]:
['aaaa', ['bc'], 'xx', ['d', ['e']], 'f']
- original_string: '{aaaa{bc}xx{d{e}}f}'
[0]:
aaaa
[1]:
['bc']
- original_string: '{bc}'
[2]:
xx
[3]:
['d', ['e']]
- original_string: '{d{e}}'
[0]:
d
[1]:
['e']
- original_string: '{e}'
[4]:
f
注意:ParseResults.dump 的当前版本存在一个限制,它只显示键 或 子项,而不是两者 - 此输出需要修复以删除限制,将在下一个 pyparsing 版本中发布。但是即使 dump() 没有显示这些子结构,它们在您的实际结构中也存在,如果您打印出结果的 repr 就可以看到:
print(repr(result[0]))
(['aaaa', (['bc'], {'original_string': '{bc}'}), 'xx', (['d', (['e'], {'original_string': '{e}'})], {'original_string': '{d{e}}'}), 'f'], {'original_string': '{aaaa{bc}xx{d{e}}f}'})