如何解析非标准格式的配置文件?
如何解析非标准格式的配置文件?
看起来你碰到了个挺棘手的非标准配置格式问题,这种自定义语法确实让人头疼!先帮你拆解下这个格式的核心特点,再看看怎么调整Parslet代码来适配它。
先梳理这个自定义格式的关键规则
这个配置的语法和JSON有相似之处,但有很多自定义扩展,先理清核心特点:
- 层级结构用
{}包裹,但键的类型非常灵活:可以是普通标识符(如lname、bufferdata)、纯数字(如1、2),甚至是带转义的特殊字符串(如0.0.0.0\0) - 键值对有两种形式:
键: 值(如lname: node_3432_ND_23),或者键 { ... }(如bufferdata { ... })——后者相当于键直接对应嵌套对象,省略了冒号 - 支持无引号字符串:只要不包含空白、
{、}、:这些语法符号,都可以直接书写(如tds.1、1_3s) - 允许空值(如
sutprofile:、dataref:),即键后面只有冒号没有内容 - 数字作为键的结构(如
1 { ... })是有序集合,类似数组但用数字索引标识
你现有Parslet代码的问题点
看了你写的Parser,几个核心问题导致无法正确解析:
- 字符串规则太宽泛:你定义的
string会把几乎所有非引号字符都当成字符串,包括{、:这些语法符号,导致Parser把结构符号误判为内容 - 未处理数字键和特殊转义键:现有
hash_pair只支持字符串作为键,但格式里大量使用数字和特殊转义字符串当键 - 未覆盖空值和冒号省略的情况:比如
sutprofile:这种空值,或者bufferdata { ... }这种省略冒号的键值对 - 数组规则不符合实际:格式里没有用
[]的数组,而是用数字键的嵌套对象表示有序集合
调整后的Parslet Parser实现
我帮你改写了Parser,适配这个格式的特点,每部分加了注释方便理解:
class CustomConfigParser < Parslet::Parser # 跳过空白:空格、制表符、换行,所有语法符号匹配前都要跳过空白 rule(:ws) { match('[\s\t\n\r]').repeat(1).skip } rule(:ws?) { ws.maybe } # 核心语法符号 rule(:lbrace) { str('{') >> ws? } rule(:rbrace) { str('}') >> ws? } rule(:colon) { str(':') >> ws? } # 无引号字符串:不能包含空白、{、}、:这些语法关键字符 rule(:unquoted_str) { match('[^\s{}:]').repeat(1).as(:unquoted_str) } # 带转义的特殊键:比如0.0.0.0\0,允许包含转义符\,但排除结构符号{、} rule(:escaped_key_char) { str('\\') >> any | match('[^{}]') } rule(:escaped_key) { escaped_key_char.repeat(1).as(:escaped_key) } # 数字键:纯数字组成的键 rule(:numeric_key) { match('[0-9]').repeat(1).as(:numeric_key) } # 所有合法的键类型 rule(:key) { unquoted_str | numeric_key | escaped_key } # 值的类型:无引号字符串、数字,或者嵌套对象 rule(:value) { unquoted_str | match('[0-9]').repeat(1).as(:number) | object } # 空值:键后面只有冒号没有内容的情况 rule(:null_value) { str('').as(:null) } # 键值对的两种形式 rule(:key_value_pair) { ( # 形式1: key: value 或者 key: (空值) key >> colon >> (value | null_value) >> ws? ) | ( # 形式2: key { ... } 省略冒号的嵌套对象 key >> ws? >> lbrace >> object_content >> rbrace ) } # 对象内容:多个键值对的集合,空白分隔 rule(:object_content) { (key_value_pair | ws?).repeat } # 完整对象:{ 内容 } rule(:object) { lbrace >> object_content >> rbrace } # 根规则:配置从一个顶级对象开始 root :object end
调试和扩展建议
- 分步测试:先拿配置里的小片段测试,比如单独解析
bufferdata { ... }结构,确认嵌套识别正确,再逐步增加复杂度 - 用Parslet调试工具:调用
parser.parse_with_debug(你的配置字符串),可以看到Parser每一步的匹配过程,快速定位问题 - 处理边缘场景:比如完全空的对象
2 { }、带转义的键127.0.0.1\32,这些需要单独测试验证 - 备选方案:如果Parslet的PEG语法写起来太绕,也可以考虑自己写递归下降解析器——这个格式的规则不算特别复杂,手动跟踪嵌套层级可能更直观
备注:内容来源于stack exchange,提问作者Hanynowsky
相关产品推荐
相关产品推荐

