python - 使用 Pyparsing 解析自定义格式(大括号分隔)文本配置

标签 python parsing pyparsing

我需要解析一些负载均衡器配置部分。这看起来很简单(至少对于人类来说)。

Config 由多个对象组成,其内容位于大括号中,如下所示:

ltm rule ssl-header-insert {
    when HTTP_REQUEST {
  HTTP::header insert "X-SSL-Connection" "yes"
}
}
ltm rule some_redirect {
    priority 1

when HTTP_REQUEST {

    if { (not [class match [IP::remote_addr] equals addresses_group ]) }
    {
        HTTP::redirect "http://some.page.example.com"
        TCP::close
        event disable all
    }
}

每个部分/对象的内容都是 TCL 代码,因此会有嵌套的花括号。我想要实现的是将其成对解析为:对象标识符(在ltm规则关键字之后)及其内容(大括号内的tcl代码)。

我查看了一些示例并进行了很多实验,但这确实给我带来了困难。我在 pyparsing 中做了一些调试(这对我来说也有点困惑),我认为我无法以某种方式检测到右大括号,但无法弄清楚。

到目前为止我想到了什么:

from pyparsing import *
import json

list_sample = """ltm rule ssl-header-insert {
    when HTTP_REQUEST {
  HTTP::header insert "X-SSL-Connection" "yes"
}
}
ltm rule some_redirect {
    priority 1

when HTTP_REQUEST {

    if { (not [class match [IP::remote_addr] equals addresses_group ]) }
    {
        HTTP::redirect "http://some.page.example.com"
        TCP::close
        event disable all
    }
}
}
ltm rule http_header_replace {
    when HTTP_REQUEST {

        HTTP::header replace Host some.host.example.com

}
}"""

ParserElement.defaultWhitespaceChars=(" \t")
NL = LineEnd()
END = StringEnd()

LBRACE, RBRACE = map(Suppress, '{}')
ANY_HEADER = Suppress("ltm rule ") + Word(alphas, alphanums + "_-")
END_MARK = Literal("ltm rule")

CONTENT_LINE = (~ANY_HEADER + (NotAny(RBRACE + FollowedBy(END_MARK)) + ~END + restOfLine) | (~ANY_HEADER + NotAny(RBRACE + FollowedBy(END)) + ~END + restOfLine)) | (~RBRACE + ~END + restOfLine)

ANY_HEADER.setName("HEADER").setDebug()
LBRACE.setName("LBRACE").setDebug()
RBRACE.setName("RBRACE").setDebug()
CONTENT_LINE.setName("LINE").setDebug()

template_defn = ZeroOrMore((ANY_HEADER + LBRACE +
                 Group(ZeroOrMore(CONTENT_LINE)) +
                 RBRACE))
template_defn.ignore(NL)


results = template_defn.parseString(list_sample).asList()

print("Raw print:")
print(results)
print("----------------------------------------------")
print("JSON pretty dump:")
print json.dumps(results, indent=2)

我在调试中看到某些匹配有效,但最终失败并导致空列表。 旁注 - 我的语法的 CONTENT_LINE 部分一般来说可能过于复杂,但到目前为止我没有找到任何更简单的方法来涵盖它。

接下来的事情是弄清楚如何在内容部分中保留新行和制表符,因为我需要在输出中保持不变。但看起来我必须使用 ignore() 函数 - 它会跳过新行 - 首先解析多行文本,所以这是另一个挑战。

如果有人帮助我找出问题所在,我将不胜感激。或者也许我应该采取其他方法?

最佳答案

我认为 nestedExpr('{', '}') 会有所帮助。这将处理嵌套的“{}”,并且在 originalTextFor 中换行将保留换行符和空格。

import pyparsing as pp

LTM, RULE = map(pp.Keyword, "ltm rule".split())
ident = pp.Word(pp.alphas, pp.alphanums+'-_')

ltm_rule_expr = pp.Group(LTM + RULE 
                         + ident('name') 
                         + pp.originalTextFor(pp.nestedExpr('{', '}'))('body'))

使用示例字符串(添加缺少的尾随“}”后):

for rule, _, _ in ltm_rule_expr.scanString(sample):
    print(rule[0].name, rule[0].body.splitlines()[0:2])

给出

ssl-header-insert ['{', '    when HTTP_REQUEST {']
some_redirect ['{', '    priority 1']

dump() 也是列出返回的 ParseResults 内容的好方法:

for rule, _, _ in ltm_rule_expr.scanString(sample):
    print(rule[0].dump())
    print()

['ltm', 'rule', 'ssl-header-insert', '{\n    when HTTP_REQUEST {\n  HTTP::header insert "X-SSL-Connection" "yes"\n}\n}']
- body: '{\n    when HTTP_REQUEST {\n  HTTP::header insert "X-SSL-Connection" "yes"\n}\n}'
- name: 'ssl-header-insert'

['ltm', 'rule', 'some_redirect', '{\n    priority 1\n\nwhen HTTP_REQUEST {\n\n    if { (not [class match [IP::remote_addr] equals addresses_group ]) }\n    {\n        HTTP::redirect "http://some.page.example.com"\n        TCP::close\n        event disable all\n    }\n}}']
- body: '{\n    priority 1\n\nwhen HTTP_REQUEST {\n\n    if { (not [class match [IP::remote_addr] equals addresses_group ]) }\n    {\n        HTTP::redirect "http://some.page.example.com"\n        TCP::close\n        event disable all\n    }\n}}'
- name: 'some_redirect'

请注意,我将 'ltm''rule' 分解为单独的关键字表达式。这可以防止开发人员可能将有效代码编写为ltmrule blah,并且“ltm”和“rule”之间的空格大于 1。这种事情经常发生,你永远不知道哪里会出现空白。

关于python - 使用 Pyparsing 解析自定义格式(大括号分隔)文本配置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46891300/

相关文章:

python - 使用 pyparsing 有什么问题

python - 评估 WFF 逻辑表达式时出现 Pyparsing 错误?

python 3 无法将字节连接到列表的 str

python - PyOpenGL 如何导入 obj 文件?

python - 如何对 python 版本开关进行单元测试

php - 如何解析包含 "parse_ini_file"特殊字符的 ini 文件?

c++ - 读取和解析文件,将解析后的字符串的每一段分配给它自己的变量

python - Libxml Cleaner 将不需要的 <p> 标记添加到 HTML 片段

python - 使可选表达式在存在但不匹配时抛出错误

python - 迭代 CGI 文件中的列表并打印 html 中的值