在 Python 中解析自定义配置格式
Parsing a custom configuration format in Python
我正在为 Stellaris 游戏编写一个配置文件管理器,但我遇到了他们保存有关模组和设置信息的格式的问题。
Mod 文件:
name="! (Ship Designer UI Fix) !"
path="mod/ship_designer_ui_fix"
tags={
"Fixes"
}
remote_file_id="879973318"
supported_version="1.6"
设置:
language="l_english"
graphics={
size={
x=1920
y=1200
}
min_gui={
x=1920
y=1200
}
gui_scale=1.000000
gui_safe_ratio=1.000000
refreshRate=59
fullScreen=no
borderless=no
display_index=0
shadowSize=2048
multi_sampling=8
maxanisotropy=16
gamma=50.000000
vsync=yes
}
last_mods={
"mod/ship_designer_ui_fix.mod"
"mod/ugc_720237457.mod"
"mod/ugc_775944333.mod"
}
我认为 pyparsing
会在这方面有所帮助(而且可能会有所帮助),但我已经很久没有真正做过这样的事情了,我对 atm 毫无头绪。
我必须提取简单的 key=value
但我正在努力从那里真正移动以能够提取数组,更不用说多级数组了。
lbrack = Literal("{").suppress()
rbrack = Literal("}").suppress()
equals = Literal("=").suppress()
nonequals = "".join([c for c in printables if c != "="]) + " \t"
keydef = ~lbrack + Word(nonequals) + equals + restOfLine
conf = Dict( ZeroOrMore( Group(keydef) ) )
tokens = conf.parseString(data)
如您所见,我还没有走多远。谁能指出我的下一步?我并不是要为整个事情寻求一个完成的和可行的解决方案——它会让我前进很多,但其中的乐趣在哪里:)
好吧, 非常想投入并编写这个解析器,但是您想要自己享受其中的乐趣,那太好了。
在写任何代码之前,写一个BNF。这样你就可以编写一个体面且健壮的解析器,而不仅仅是 "everything that's not an equals sign must be an identifier".
这里有很多"something = something"位,看看'='左右两边的东西种类。左侧看起来都像是非常规矩的标识符:字母、下划线。我也可以设想数字,只要它们不是主角。所以假设左侧将是标识符:
identifier_leading = 'A'..'Z' 'a'..'z' '_'
identifier_body = identifier_leading '0'..'9'
identifier ::= identifier_leading + identifier_body*
右边是混合的东西:
- 整数
- 浮动
- 'yes' 或 'no' 布尔值
- 带引号的字符串
- 大括号里的东西
"something in braces" 要么是引用字符串的列表,要么是 'identifer = value' 对的列表。我将跳过定义浮点数和整数以及带引号的字符串的可怕细节,让我们假设我们已经定义了那些:
boolean_value ::= 'yes' | 'no'
value ::= float | integer | boolean_value | quoted_string | string_list_in_braces | key_value_list_in_braces
string_list_in_braces ::= '{' quoted_string * '}'
key_value ::= identifier '=' value
key_value_list_in_braces ::= '{' key_value* '}'
你必须使用 pyparsing Forward
来声明 value
才能完全定义,因为它在 key_value
中使用,但 key_value
用于key_value_list_in_braces
,用于定义value
——递归文法。您已经熟悉 Dict(OneOrMore(Group(named_item)))
模式,这应该有助于为您提供可通过名称访问的字段结构。对于 identifier
,Word
可以工作,或者您可以只使用预定义的 pyparsing_common.identifier
,它是去年作为 pyparsing_common
命名空间 class 的一部分引入的.
从 BNF 到 pyparsing 的转换从这里应该是一对一的。就此而言,从 BNF 开始,您也可以使用 PLY、ANTLR 或其他解析库。 BNF 真的值得花 1/2 小时或 1/2 天来整理。
我正在为 Stellaris 游戏编写一个配置文件管理器,但我遇到了他们保存有关模组和设置信息的格式的问题。
Mod 文件:
name="! (Ship Designer UI Fix) !"
path="mod/ship_designer_ui_fix"
tags={
"Fixes"
}
remote_file_id="879973318"
supported_version="1.6"
设置:
language="l_english"
graphics={
size={
x=1920
y=1200
}
min_gui={
x=1920
y=1200
}
gui_scale=1.000000
gui_safe_ratio=1.000000
refreshRate=59
fullScreen=no
borderless=no
display_index=0
shadowSize=2048
multi_sampling=8
maxanisotropy=16
gamma=50.000000
vsync=yes
}
last_mods={
"mod/ship_designer_ui_fix.mod"
"mod/ugc_720237457.mod"
"mod/ugc_775944333.mod"
}
我认为 pyparsing
会在这方面有所帮助(而且可能会有所帮助),但我已经很久没有真正做过这样的事情了,我对 atm 毫无头绪。
我必须提取简单的 key=value
但我正在努力从那里真正移动以能够提取数组,更不用说多级数组了。
lbrack = Literal("{").suppress()
rbrack = Literal("}").suppress()
equals = Literal("=").suppress()
nonequals = "".join([c for c in printables if c != "="]) + " \t"
keydef = ~lbrack + Word(nonequals) + equals + restOfLine
conf = Dict( ZeroOrMore( Group(keydef) ) )
tokens = conf.parseString(data)
如您所见,我还没有走多远。谁能指出我的下一步?我并不是要为整个事情寻求一个完成的和可行的解决方案——它会让我前进很多,但其中的乐趣在哪里:)
好吧, 非常想投入并编写这个解析器,但是您想要自己享受其中的乐趣,那太好了。
在写任何代码之前,写一个BNF。这样你就可以编写一个体面且健壮的解析器,而不仅仅是 "everything that's not an equals sign must be an identifier".
这里有很多"something = something"位,看看'='左右两边的东西种类。左侧看起来都像是非常规矩的标识符:字母、下划线。我也可以设想数字,只要它们不是主角。所以假设左侧将是标识符:
identifier_leading = 'A'..'Z' 'a'..'z' '_'
identifier_body = identifier_leading '0'..'9'
identifier ::= identifier_leading + identifier_body*
右边是混合的东西:
- 整数
- 浮动
- 'yes' 或 'no' 布尔值
- 带引号的字符串
- 大括号里的东西
"something in braces" 要么是引用字符串的列表,要么是 'identifer = value' 对的列表。我将跳过定义浮点数和整数以及带引号的字符串的可怕细节,让我们假设我们已经定义了那些:
boolean_value ::= 'yes' | 'no'
value ::= float | integer | boolean_value | quoted_string | string_list_in_braces | key_value_list_in_braces
string_list_in_braces ::= '{' quoted_string * '}'
key_value ::= identifier '=' value
key_value_list_in_braces ::= '{' key_value* '}'
你必须使用 pyparsing Forward
来声明 value
才能完全定义,因为它在 key_value
中使用,但 key_value
用于key_value_list_in_braces
,用于定义value
——递归文法。您已经熟悉 Dict(OneOrMore(Group(named_item)))
模式,这应该有助于为您提供可通过名称访问的字段结构。对于 identifier
,Word
可以工作,或者您可以只使用预定义的 pyparsing_common.identifier
,它是去年作为 pyparsing_common
命名空间 class 的一部分引入的.
从 BNF 到 pyparsing 的转换从这里应该是一对一的。就此而言,从 BNF 开始,您也可以使用 PLY、ANTLR 或其他解析库。 BNF 真的值得花 1/2 小时或 1/2 天来整理。