【问题标题】:PyParsing Parse nested loop with brace and specific headerPyParsing 使用大括号和特定标头解析嵌套循环
【发布时间】:2014-07-03 17:33:25
【问题描述】:

我发现了几个关于 pyparsing 的主题。他们在解析嵌套循环时处理几乎相同的问题,但即使这样,我也找不到解决我的错误的方法。

我有以下格式:

key value;
header_name "optional_metadata"
{
     key value;
     sub_header_name
     {
        key value;
     };
};
key value;
  • 键是字母
  • 值可能是 Int、String 类型,带有字母数字 + "@._"
  • 键/值可能在大括号之后
  • key/value 可能在文件中的第一个大括号块之前
  • 大括号之前或之后的键/值是可选的
  • 标题可能有名称
  • 右大括号后跟分号

我使用了以下解析器:

VALID_KEY_CHARACTERS = alphanums
VALID_VALUE_CHARACTERS = srange("[a-zA-Z0-9_\"\'\-\.@]")

lbr = Literal( '{' ).suppress()
rbr = Literal( '}' ).suppress() + Literal(";").suppress()

expr = Forward()
atom = Word(VALID_KEY_CHARACTERS) + Optional(Word(VALID_VALUE_CHARACTERS))
pair = atom | lbr + OneOrMore( expr ) + rbr
expr << Group( atom + pair )

当我使用它时,我只得到了“header_name”和“header_metadata”,我对其进行了修改,并且在大括号内只得到了键/值,python 异常被触发以显示解析错误(它需要'}'到达 sub_header_name 时。

谁能帮我理解为什么? 谢谢。

【问题讨论】:

    标签: python nested-loops pyparsing braces


    【解决方案1】:

    我认为主要问题是你的语法没有完全描述输入,导致几个不匹配。我看到的两个主要问题是您忘记了每个密钥对值必须以分号结尾,并且没有指定密钥对值可以出现在右花括号之后。它也看起来像线条:

    pair = atom | lbr + OneOrMore( expr ) + rbr
    expr << Group( atom + pair )
    

    ...要求每组花括号至少包含两个密钥对值或一个密钥对值和一组花括号。我相信一旦您遇到以下行,这会导致错误:

    {
        key value;
    };
    

    ...根据您的意见,虽然我不完全确定。

    无论如何,在玩弄了你的语法之后,我最终得到了这个:

    from pyparsing import *
    
    data = """key1 value1; 
    header_name "optional_metadata"
    {
         key2 value2;
         sub_header_name
         {
            key value;
         };
    };
    key3 value3;"""
    
    # I'm reusing the key characters for the header names, which can contain a semicolon
    VALID_KEY_CHARACTERS = srange("[a-zA-Z0-9_]")
    VALID_VALUE_CHARACTERS = srange("[a-zA-Z0-9_\"\'\-\.@]")
    
    semicolon = Literal(';').suppress()
    lbr = Literal('{').suppress()
    rbr = Literal('}').suppress()
    
    key = Word(VALID_KEY_CHARACTERS)
    value = Word(VALID_VALUE_CHARACTERS)
    
    key_pair = Group(key + value + semicolon)("key_pair")
    metadata = Group(key + Optional(value))("metadata")
    
    header = key_pair + Optional(metadata)
    
    expr = Forward()
    contents = Group(lbr + expr + rbr + semicolon)("contents")
    expr << header + Optional(contents) + Optional(key_pair)
    
    print expr.parseString(data).asXML()
    

    这会产生以下输出:

    <key_pair>
      <key_pair>
        <ITEM>key1</ITEM>
        <ITEM>value1</ITEM>
      </key_pair>
      <metadata>
        <ITEM>header_name</ITEM>
        <ITEM>&quot;optional_metadata&quot;</ITEM>
      </metadata>
      <contents>
        <key_pair>
          <ITEM>key2</ITEM>
          <ITEM>value2</ITEM>
        </key_pair>
        <metadata>
          <ITEM>sub_header_name</ITEM>
        </metadata>
        <contents>
          <key_pair>
            <ITEM>key</ITEM>
            <ITEM>value</ITEM>
          </key_pair>
        </contents>
      </contents>
      <key_pair>
        <ITEM>key3</ITEM>
        <ITEM>value3</ITEM>
      </key_pair>
    </key_pair>
    

    我不完全确定这是否正是您想要完成的,希望它应该足够接近,以便您可以调整它以适应您的特定任务。

    【讨论】:

    • +1 用于拆分 ';'从'}',从OP的例子来看,它们绝对可以独立发生,因此应该分开处理。 asXML 并不总是打印出解析结果的最佳方式,请尝试使用 dump。
    【解决方案2】:

    我试图使用 python 解析 terraform 资源并遇到了和你一样的问题。

    这是我的parser的要点

    测试用例文件“repository.tf”是您可以看到解析器如何解析具有特定标头的嵌套大括号的文件

    https://gist.github.com/antigenius0910/5e00e80cfadf48642acb44132acefb3a#file-parse-py-L95-L101

    ~/Downloads/5e00e80cfadf48642acb44132acefb3a-b514369c817885589911ca2c81fa367af4851d86 ᐅ python parse.py 
    
    resource "github_repository" "tfer--test-002D-plugin-002D-example" {
      allow_merge_commit     = "true"
      allow_rebase_merge     = "true"
      allow_squash_merge     = "true"
      archived               = "true"
      default_branch         = "main"
      delete_branch_on_merge = "false"
      has_downloads          = "true"
      has_issues             = "true"
      has_projects           = "false"
      has_wiki               = "true"
      is_template            = "false"
      name                   = "test-plugin-example"
      private                = "true"
    
      template {
        owner      = "test"
        repository = "test-plugin-templattest-plugin-template"
      }
    
      visibility           = "internal"
      vulnerability_alerts = "false"
    }
    
    

    希望这会有所帮助:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-19
      • 2022-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-09
      相关资源
      最近更新 更多