我的配置文件结构类似于简化的C语法,例如:
Main { /* some comments */
VariableName1 = VariableValue1;
VariableName2 = VariableValue2;
SubSection {
VariableName1 = VariableValue1; // inline comment
VariableName2 = VariableValue2;
}
VariableName3 = "StriingValue4";
}
可以递归嵌套部分。
如何以干净,“诡异”的方式将该文件解析为dict
?
[编辑]
好的,我找到了pyparsing模块:)但是如果没有它,也许有人可以告诉你如何做到这一点。
[EDIT2]
由于好奇心,我想知道将来如何编写我认为简单的任务。
答案 0 :(得分:2)
使用SimpleParse之类的解析器,只需输入EBNF定义即可。
您是否在某种BNF中记录了格式,不是吗?如果没有,你可以告诉下一个天才发明另一个配置格式,而不是使用json,xml,yaml或xml他无权重新发明轮子,除非他可以使用EBNF指定语法。
如果您不熟悉EBNF,可能需要一些时间来编写语法,但它会付出代价。它将使您的代码记录良好,坚如磐石,易于维护。
有关其他选项,请参阅有关Language Parsing的python wiki。
如果你尝试使用str.split或正则表达式进行一些特技表演,那么维护这段代码的每个其他开发人员都会诅咒你。
更新:
我突然意识到,如果您将SectionName
替换为SectionName :
,;
替换为,
并将主要部分用一对花括号括起来,则此格式will likely to be valid json。
"Name" = JSON Grammar
"Author" = Arsène von Wyss
"Version" = 1.0
"About" = 'Grammar for JSON data, following http://www.json.org/'
! and compliant with http://www.ietf.org/rfc/rfc4627
"Start Symbol" = <Json>
"Case Sensitive" = True
"Character Mapping" = 'Unicode'
! ------------------------------------------------- Sets
{Unescaped} = {All Valid} - {&1 .. &19} - ["\]
{Hex} = {Digit} + [ABCDEFabcdef]
{Digit9} = {Digit} - [0]
! ------------------------------------------------- Terminals
Number = '-'?('0'|{Digit9}{Digit}*)('.'{Digit}+)?([Ee][+-]?{Digit}+)?
String = '"'({Unescaped}|'\'(["\/bfnrt]|'u'{Hex}{Hex}{Hex}{Hex}))*'"'
! ------------------------------------------------- Rules
<Json> ::= <Object>
| <Array>
<Object> ::= '{' '}'
| '{' <Members> '}'
<Members> ::= <Pair>
| <Pair> ',' <Members>
<Pair> ::= String ':' <Value>
<Array> ::= '[' ']'
| '[' <Elements> ']'
<Elements> ::= <Value>
| <Value> ',' <Elements>
<Value> ::= String
| Number
| <Object>
| <Array>
| true
| false
| null
答案 1 :(得分:2)
你需要使用这个Backus Naur表单递归地解析它,从PARSE开始:
PARSE: '{' VARASSIGN VARASSIGN [PARSE [VARASSIGN]] '}'
VARASSIGN : VARIABLENAME '=' '"' STRING '"'
VARIABLENAME: STRING
STRING: [[:alpha:]][[:alnum:]]*
因为您的结构很简单,所以您可以使用预测解析器LL(1)。
答案 2 :(得分:1)
1)编写一个tokenizer,即一个将解析字符流并将其转换为Identifiers,OpeningBrace,ClosingBrace,EqualSign和SemiColon列表的函数;注释和空格被丢弃。可以使用Regexpr来完成。
2)编写一个简单的解析器。跳过第一个Identifier和OpeningBrace。
解析器需要一个标识符,后面跟一个EqualSign或OpeningBrace,或者一个ClosingBrace。
2.1)如果EqualSign,必须跟随Identifier和SemiColon。 2.2)如果是OpeningBrace,则递归调用解析器。 2.3)如果ClosingBrace,则从递归调用返回。
在2.1的处理中,按照您喜欢的方式在dict中输入所需的数据。您可以使用封闭块的名称作为标识符的前缀,例如:
{"Main.SubSection.VariableName1": VariableValue1}
这是解析器的原型代码,在tokenizer之后调用。它会扫描字符串,其中字母代表标识符,分隔符为 = {}; ,最后一个标记必须为 $ 。
def Parse(String, Prefix= "", Nest= 0):
global Cursor
if Nest == 0:
Cursor= 0
# Scan the input string
while String[Cursor + 0].isalpha():
# Identifier, starts an Assignment or a Block (Id |)
if String[Cursor + 1] == "=":
# Assignment, lookup (Id= | Id;)
if String[Cursor + 2].isalpha():
if String[Cursor + 3] == ";":
# Accept the assignment (Id=Id; |)
print Nest * " " + Prefix + String[Cursor] + "=" + String[Cursor + 2] + ";"
Cursor+= 4
elif String[Cursor + 1] == "{":
# Block, lookup (Id{ | )
print Nest * " " + String[Cursor] + "{"
Cursor+= 2
# Recurse
Parse(String, Prefix + String[Cursor - 2] + "::", Nest + 4)
else:
# Unexpected token
break
if String[Cursor + 0] == "}":
# Block complete, (Id{...} |)
print (Nest - 4) * " " + "}"
Cursor+= 1
return
if Nest == 0 and String[Cursor + 0] == "$":
# Done
return
print "Syntax error at", String[Cursor:], ":("
Parse("C{D=E;X{Y=Z;}F=G;}H=I;A=B;$")
执行时,输出:
C{
C::D=E;
X{
C::X::Y=Z;
}
C::F=G;
}
H=I;
A=B;
证明它确实检测到了嵌套。用您喜欢的任何处理替换打印语句。
答案 3 :(得分:0)
您可以使用pyparsing为此格式编写解析器。