使用PyParsing解析带括号的嵌套循环和特定头部
我发现了几个关于pyparsing的话题,它们都在处理类似的嵌套循环解析问题,但即使这样,我还是找不到解决我错误的方法。
我有以下格式:
key value;
header_name "optional_metadata"
{
key value;
sub_header_name
{
key value;
};
};
key value;
- 键是字母和数字的组合
- 值可以是整数、字符串,或者包含字母、数字和"@._"的组合
- 键/值可能在一个大括号块之后
- 键/值也可能在文件的第一个大括号块之前
- 大括号前后的键/值都是可选的
- 头部可以有一个名字
- 闭合的大括号后面跟着一个分号
我使用了以下解析器:
VALID_KEY_CHARACTERS = alphanums
VALID_VALUE_CHARACTERS = srange("[a-zA-Z0-9_\"\'\-\.@]")
lbr = Literal( '{' ).suppress()
rbr = Literal( '}' ).suppress() + Literal(";").suppress()
expr = Forward()
atom = Word(VALID_KEY_CHARACTERS) + Optional(Word(VALID_VALUE_CHARACTERS))
pair = atom | lbr + OneOrMore( expr ) + rbr
expr << Group( atom + pair )
当我使用它时,我只得到了"header_name"和"header_metadata",我修改了一下,结果只得到了大括号内的键/值,Python抛出了一个异常,显示解析错误(在到达sub_header_name时,它期待'}')。
有没有人能帮我理解一下为什么会这样?谢谢。
2 个回答
0
我在用Python解析Terraform资源的时候,遇到了和你一样的问题。
这里有我写的解析器的代码。
测试用的文件“repository.tf”可以让你看到解析器是如何处理带有特定头部的嵌套大括号的。
https://gist.github.com/antigenius0910/5e00e80cfadf48642acb44132acefb3a#file-parse-py-L95-L101
~/Downloads/5e00e80cfadf48642acb44132acefb3a-b514369c817885589911ca2c81fa367af4851d86 ᐅ python parse.py
resource "github_repository" "tfer--test-002D-plugin-002D-example" {
allow_merge_commit = "true"
allow_rebase_merge = "true"
allow_squash_merge = "true"
archived = "true"
default_branch = "main"
delete_branch_on_merge = "false"
has_downloads = "true"
has_issues = "true"
has_projects = "false"
has_wiki = "true"
is_template = "false"
name = "test-plugin-example"
private = "true"
template {
owner = "test"
repository = "test-plugin-templattest-plugin-template"
}
visibility = "internal"
vulnerability_alerts = "false"
}
希望这能对你有所帮助:)
1
我觉得主要的问题在于你的语法没有完全描述输入内容,这导致了很多不匹配的情况。我看到的两个主要问题是,你忘记了每个键值对的值必须以分号结束,而且没有说明键值对的值可以在闭合的大括号后面出现。此外,看起来这些行:
pair = atom | lbr + OneOrMore( expr ) + rbr
expr << Group( atom + pair )
...要求每一组大括号至少包含两个键值对,或者一个键值对和一组大括号。我认为这会在你遇到以下行时导致错误:
{
key value;
};
...在你的输入中,尽管我不是完全确定。
无论如何,在调整你的语法后,我得到了这个:
from pyparsing import *
data = """key1 value1;
header_name "optional_metadata"
{
key2 value2;
sub_header_name
{
key value;
};
};
key3 value3;"""
# I'm reusing the key characters for the header names, which can contain a semicolon
VALID_KEY_CHARACTERS = srange("[a-zA-Z0-9_]")
VALID_VALUE_CHARACTERS = srange("[a-zA-Z0-9_\"\'\-\.@]")
semicolon = Literal(';').suppress()
lbr = Literal('{').suppress()
rbr = Literal('}').suppress()
key = Word(VALID_KEY_CHARACTERS)
value = Word(VALID_VALUE_CHARACTERS)
key_pair = Group(key + value + semicolon)("key_pair")
metadata = Group(key + Optional(value))("metadata")
header = key_pair + Optional(metadata)
expr = Forward()
contents = Group(lbr + expr + rbr + semicolon)("contents")
expr << header + Optional(contents) + Optional(key_pair)
print expr.parseString(data).asXML()
这会产生以下输出:
<key_pair>
<key_pair>
<ITEM>key1</ITEM>
<ITEM>value1</ITEM>
</key_pair>
<metadata>
<ITEM>header_name</ITEM>
<ITEM>"optional_metadata"</ITEM>
</metadata>
<contents>
<key_pair>
<ITEM>key2</ITEM>
<ITEM>value2</ITEM>
</key_pair>
<metadata>
<ITEM>sub_header_name</ITEM>
</metadata>
<contents>
<key_pair>
<ITEM>key</ITEM>
<ITEM>value</ITEM>
</key_pair>
</contents>
</contents>
<key_pair>
<ITEM>key3</ITEM>
<ITEM>value3</ITEM>
</key_pair>
</key_pair>
我不太确定这是否正是你想要实现的,希望这能接近你的需求,这样你可以根据自己的具体任务进行调整。