3

我的配置文件结构类似于简化的 C 语法,例如:

Main { /* some comments */
    VariableName1 = VariableValue1;
    VariableName2 = VariableValue2;

    SubSection {
        VariableName1 = VariableValue1; // inline comment
        VariableName2 = VariableValue2;
    }

    VariableName3 = "StriingValue4";
}

部分可以递归嵌套。

如何dict以干净且“pythonish”的方式将该文件解析为?

[编辑]

好的,我找到了 pyparsing模块 :) 但也许有人可以告诉如何在没有它的情况下执行此操作。

[编辑2]

由于好奇,我想知道将来如何编写我认为简单的手工任务。

4

4 回答 4

2

使用像SimpleParse这样的解析器,只需为其提供 EBNF 定义。

您是否有某种 BNF 中记录的格式,不是吗?如果没有,您可以告诉下一个发明另一种配置格式而不是使用 json、xml、yaml 或 xml 的天才,除非他可以使用EBNF指定语法,否则他无权重新发明轮子。

如果您不熟悉EBNF,可能需要一些时间来编写语法,但这是值得的。它将使您的代码有据可查、坚如磐石且更易于维护。

有关其他选项,请参阅有关语言解析的 python wiki。

如果您尝试使用 str.split 或正则表达式来做一些噱头,那么所有其他维护这段代码的开发人员都会诅咒您。

更新:

我突然想到,如果你替换SectionNamewith SectionName :;with,并用一对花括号括起来主要部分,这种格式可能是有效的 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
于 2012-07-21T09:09:26.440 回答
2

你需要递归地解析它,使用这个 Backus Naur 形式,从 PARSE 开始:

PARSE: '{' VARASSIGN VARASSIGN [PARSE [VARASSIGN]] '}'

VARASSIGN : VARIABLENAME '=' '"' STRING '"'

VARIABLENAME: STRING

STRING: [[:alpha:]][[:alnum:]]*

因为您的结构很简单,所以您可以使用谓词解析器 LL(1)。

于 2012-07-21T09:12:53.740 回答
1

1) 编写一个分词器,即解析字符流并将其转换为标识符列表、OpeningBrace、ClosingBrace、EqualSign 和 SemiColon 的函数;注释和空格被丢弃。可以使用 Regexpr 来完成。

2)编写一个简单的解析器。跳过第一个标识符和 OpeningBrace。

解析器需要一个标识符,后跟 EqualSign 或 OpeningBrace 或 ClosingBrace 之一。

2.1) 如果是等号,后面必须跟标识符和分号。2.2) 如果是OpeningBrace,递归调用解析器。2.3)如果ClosingBrace,从递归调用返回。

在2.1的处理中,将想要的数据输入到dict中,按照自己喜欢的方式。您可以在标识符前面加上封闭块的名称,例如

{"Main.SubSection.VariableName1": VariableValue1}

这是解析器的原型代码,在标记器之后调用。它扫描一个字符串,其中一个字母代表一个标识符,分隔符是={}; 最后一个标记必须是$

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;

证明它确实检测到了嵌套。用您喜欢的任何处理替换打印语句。

于 2012-07-21T09:12:32.050 回答
0

您可以使用pyparsing为这种格式编写解析器。

于 2012-07-21T09:12:17.357 回答