【问题标题】:the trick to nested structures in pyparsingpyparsing 中嵌套结构的技巧
【发布时间】:2014-08-08 03:04:58
【问题描述】:

我正在努力使用 PyParsing 解析嵌套结构。我搜索了很多'nested' example uses of PyParsing,但我不知道如何解决我的问题。

这是我的内部结构:

texture_unit optionalName
{
    texture required_val
    prop_name1 prop_val1
    prop_name2 prop_val1
}

这是我的外部结构的样子,但它可以包含零个或多个内部结构。

pass optionalName
{
    prop_name1 prop_val1
    prop_name2 prop_val1

    texture_unit optionalName
    {
        // edit 2: showing use of '.' character in value
        texture required_val.file.name optional_val // edit 1: forgot this line in initial post.

        // edit 2: showing potentially multiple values
        prop_name3 prop_val1 prop_val2
        prop_name4 prop_val1
    }
}

我成功解析了内部结构。这是我的代码。

prop_ = pp.Group(pp.Word(pp.alphanums+'_')+pp.Group(pp.OneOrMore(pp.Word(pp.alphanums+'_'+'.'))))
texture_props_ = pp.Group(pp.Literal('texture') + pp.Word(pp.alphanums+'_'+'.')) + pp.ZeroOrMore(prop_)
texture_ = pp.Forward()
texture_ << pp.Literal('texture_unit').suppress() + pp.Optional(pp.Word(pp.alphanums+'_')).suppress() + pp.Literal('{').suppress() + texture_props_ + pp.Literal('}').suppress()

这是我解析外部结构的尝试,

pass_props_ = pp.ZeroOrMore(prop_)
pass_ = pp.Forward()
pass_ << pp.Literal('pass').suppress() + pp.Optional(pp.Word(pp.alphanums+'_'+'.')).suppress() + pp.Literal('{').suppress() + pass_props_ + pp.ZeroOrMore(texture_) + pp.Literal('}').suppress()

当我说: pass_.parseString(testPassStr)

我在控制台中看到应为“}”的错误。

我认为这与C struct example 非常相似,但我不确定缺少的魔法是什么。我也很好奇使用nestedExpr时如何控制生成的数据结构。

【问题讨论】:

  • 这是另一个支持嵌套结构的示例。看起来它使用了“pyparsing.Dict”。所有这些例子都展示了实现嵌套解析的不同方式,有什么共同点? pyparsing.wikispaces.com/share/view/40834661

标签: python nested pyparsing


【解决方案1】:

有两个问题:

  1. 在您的语法中,您在 texture_unit 块中将 texture 文字标记为必需,但在您的第二个示例中没有 texture
  2. 在第二个示例中,pass_props_texture_unit optionalName 重合。在它之后,pp.Literal('}') 期望 },但给出 {。这就是错误的原因。

我们可以通过更改pass_ 规则来检查它,如下所示:

pass_ << pp.Literal('pass').suppress() + pp.Optional(pp.Word(pp.alphanums+'_'+'.')).suppress() + \
             pp.Literal('{').suppress() + pass_props_

print pass_.parseString(s2)

它给了我们以下输出:

[['prop_name', ['prop_val', 'prop_name', 'prop_val', 'texture_unit', 'optionalName']]]

我们可以看到pass_props_texture_unit optionalName 重合。
所以,我们要做的是:prop_ 可以包含alphanums_.,但不能与texture_unit 文字匹配。我们可以用regexnegative lookahead 做到这一点:

prop_ = pp.Group(  pp.Regex(r'(?!texture_unit)[a-z0-9_]+')+ pp.Group(pp.OneOrMore(pp.Regex(r'(?!texture_unit)[a-z0-9_.]+'))) )

最后,工作示例将如下所示:

import pyparsing as pp

s1 = '''texture_unit optionalName
    {
    texture required_val
    prop_name prop_val
    prop_name prop_val
}'''

prop_ = pp.Group(  pp.Regex(r'(?!texture_unit)[a-z0-9_]+')+ pp.Group(pp.OneOrMore(pp.Regex(r'(?!texture_unit)[a-z0-9_.]+'))) )
texture_props_ = pp.Group(pp.Literal('texture') + pp.Word(pp.alphanums+'_'+'.')) + pp.ZeroOrMore(prop_)
texture_ = pp.Forward()
texture_ = pp.Literal('texture_unit').suppress() + pp.Word(pp.alphanums+'_').suppress() +\
           pp.Literal('{').suppress() + pp.Optional(texture_props_) + pp.Literal('}').suppress()

print texture_.parseString(s1)

s2 = '''pass optionalName
{
    prop_name1 prop_val1.name
    texture_unit optionalName1
    {
        texture required_val1
        prop_name2 prop_val12
        prop_name3 prop_val13
    }
    texture_unit optionalName2
    {
        texture required_va2l
        prop_name2 prop_val22
        prop_name3 prop_val23
    }
}'''

pass_props_ = pp.ZeroOrMore(prop_  )
pass_ = pp.Forward()

pass_ = pp.Literal('pass').suppress() + pp.Optional(pp.Word(pp.alphanums+'_'+'.')).suppress() +\
        pp.Literal('{').suppress() + pass_props_ + pp.ZeroOrMore(texture_ ) + pp.Literal('}').suppress()

print pass_.parseString(s2)

输出:

[['texture', 'required_val'], ['prop_name', ['prop_val', 'prop_name', 'prop_val']]]
[['prop_name1', ['prop_val1.name']], ['texture', 'required_val1'], ['prop_name2', ['prop_val12', 'prop_name3', 'prop_val13']], ['texture', 'required_va2l'], ['prop_name2', ['prop_val22', 'prop_name3', 'prop_val23']]]

【讨论】:

  • 1.你是对的,我的嵌套示例缺少必需的“纹理”属性。这是发帖时的错别字。我会在帖子中更正。
  • @cyrf 第二个项目和他的解决方案呢?
  • 关于#2,谢谢你的好建议。我还在测试它。我试图理解为什么在支持嵌套 C 结构的 C 结构解析器示例中不需要“负前瞻”(在我的原始帖子中链接)。
  • 'prop_' 的新定义打破了对内部结构的解析。我应该让测试更明确,我试图让它通用和可读。我现在将编辑我的帖子以更好地指定我需要的内容。你能评论一下为什么 C 结构示例不需要“负前瞻”吗?
  • 如果内部结构不使用'.'在prop_val中,然后解析内部结构。但是,使用您的更改解析外部结构仍然会产生错误。
【解决方案2】:

我正在寻找的答案与“转发”解析器的使用有关,如 Cstruct 示例中所示(在 OP 中链接)。

定义嵌套结构语法的难点在于定义结构所有可能的成员类型,这需要包括结构本身,但还没有定义。

为嵌套结构定义pyparsing语法的“技巧”是延迟结构的定义,但在定义结构成员时包含结构的“前向声明”版本,因此成员也可以包括一个结构。然后以成员列表的形式完成结构语法。

struct = Forward()
member = blah | blah2 | struct
struct << ZeroOrMore( Group(member) )

这也在这里讨论: Pyparsing: Parsing semi-JSON nested plaintext data to a list

OP(我的)描述的测试数据和语法不够具体,并且在应该失败时匹配。 @NorthCat 正确地发现了语法中不需要的匹配项。然而,定义许多“负前瞻”的建议似乎难以管理。

我的解决方案没有定义不应该匹配的内容,而是明确列出了可能的匹配项。匹配是成员关键字,使用'oneOf('由空格分隔的单词列表')。一旦我指定了所有可能的匹配,我意识到我的结构不是一个嵌套结构,而是一个具有有限深度和不同语法描述每个深度的结构。所以,我的成员定义不需要前向声明技巧。

我的成员定义的终止符与 Cstruct 示例中的不同。而不是以';'结束(分号)就像在 C++ 中一样,我的成员定义需要在行尾终止。在 pyparsing 中,您可以使用“LineEnd”解析器指定行尾。所以,我将我的成员定义为一个不包括'LineEnd'的值列表,就像这样,注意在最后一个定义中使用了“Not”(~)运算符:

EOL = LineEnd().suppress()
ident = Word( alphas+"_", alphanums+"_$@#." )
integer = Word(nums)
real = Combine(Optional(oneOf('+ -')) + Word(nums) + '.' + Optional(Word(nums)))
propVal = real | integer | ident
propList = Group(OneOrMore(~EOL + propVal))

【讨论】:

    猜你喜欢
    • 2013-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-14
    • 2010-09-11
    • 2015-09-24
    • 1970-01-01
    相关资源
    最近更新 更多