PyParsing解析代码块时,如何仅提取模块名及.obs前缀的参数
解决PyParsing提取特定前缀参数的问题
看起来你已经走对了大半路,问题出在没有对参数做针对性过滤——你的当前parser会匹配所有参数,而不是只保留.obs开头的。下面给你两种可行的实现思路,都能精准得到你想要的结果:
方法一:解析时直接过滤并保留目标参数
这种方法在定义parser时就区分“需要保留的obs参数”和“需要跳过的其他参数”,解析后直接得到目标结果,不用二次处理:
from pyparsing import Word, alphanums, Suppress, ZeroOrMore, Optional, identifier # 定义基础标识符(字母、数字、下划线) basic_identifier = Word(alphanums + '_') # 定义需要保留的obs参数名:必须以.obs开头 obs_param_name = ('.' + basic_identifier).addCondition(lambda s: s.startswith('.obs')) # 定义非obs参数:匹配后直接跳过,不保留结果 non_obs_param = Suppress( ('.' + basic_identifier).addCondition(lambda s: not s.startswith('.obs')) + Suppress('(') + basic_identifier + Suppress(')') + Optional(Suppress(',')) # 处理参数后的逗号 ) # 定义obs参数:匹配后保留参数名,处理后续逗号 obs_param = ( obs_param_name + Suppress('(') + basic_identifier + Suppress(')') + Optional(Suppress(',')) ) # 模块整体解析规则 module_parser = ( basic_identifier.setResultsName("module_name") + Suppress('(') + ZeroOrMore(non_obs_param | obs_param).setResultsName("obs_params") + Suppress(')') + Suppress(';') ) # 测试解析 test_text = """abc_module ( .test_test1 ( test_test_real1 ), .abc_test ( abc_test_real ), .obs_test_one ( obs_test_one_real ), .obs_test_two ( obs_test_two_real ) ); cfg_module ( .test_test2 ( test_test_real2 ), .xyz_test ( xyz_test_real ) ); xyz_module ( .test_test2 ( test_test_real2 ), .xyz_test ( xyz_test_real ), .obs_test_three ( obs_test_three_real ), .ahc_test ( ahc_test_real ), .obs_test_four ( obs_test_four_real ) );""" # 提取所有模块的结果 results = module_parser.searchString(test_text) # 整理成你需要的格式 parse_result = [[mod.module_name] + list(mod.obs_params) for mod in results] print(parse_result)
运行这段代码会直接输出你期望的结果:
[['abc_module', '.obs_test_one', '.obs_test_two'], ['cfg_module'], ['xyz_module', '.obs_test_three', '.obs_test_four']]
方法二:先解析所有参数,再二次过滤
如果你想保留原有parser的大部分结构,也可以先解析出所有参数,再用Python列表推导式过滤出.obs开头的项:
from pyparsing import Word, alphanums, Suppress, OneOrMore, Optional, identifier # 基础标识符定义 basic_identifier = Word(alphanums + '_') keyword = Word(alphanums + '_' + '.') # 调整原有parser,先捕获所有参数 sub_module_parser = ( basic_identifier.setResultsName("module_name") + Suppress("(") + OneOrMore( keyword.setResultsName("param", listAllMatches=True) + Suppress("(" + keyword + ")") + Optional(Suppress(",")) ) + Suppress(")") + Suppress(";") ) # 解析文本 test_text = """abc_module ( .test_test1 ( test_test_real1 ), .abc_test ( abc_test_real ), .obs_test_one ( obs_test_one_real ), .obs_test_two ( obs_test_two_real ) ); cfg_module ( .test_test2 ( test_test_real2 ), .xyz_test ( xyz_test_real ) ); xyz_module ( .test_test2 ( test_test_real2 ), .xyz_test ( xyz_test_real ), .obs_test_three ( obs_test_three_real ), .ahc_test ( ahc_test_real ), .obs_test_four ( obs_test_four_real ) );""" results = sub_module_parser.searchString(test_text) # 过滤出.obs开头的参数 parse_result = [] for mod in results: obs_params = [p for p in mod.param if p.startswith('.obs')] parse_result.append([mod.module_name] + obs_params) print(parse_result)
这种方法更灵活,如果你之后需要处理其他类型的参数,只需要修改过滤条件即可。
关键说明
- 避免用
skipTo:skipTo是模糊匹配,容易跳过过多内容导致解析错误,针对这种有明确格式的文本,用addCondition做精准过滤更可靠。 setResultsName的listAllMatches=True:当同一个结果名会匹配多次时,需要加上这个参数才能收集到所有匹配项,否则只会保留最后一个。
内容的提问来源于stack exchange,提问作者Dav7538
相关产品推荐
相关产品推荐

