Pyparsing:如何匹配逗号分隔列表周围的括号

问题描述 投票:0回答:2

我不知道如何将表达式与 comma_separated_list 结合起来以匹配括号中的列表。以下不起作用,因为 csv 表达式吃掉了最后一个括号:

import pyparsing as pp

rule = "{" + pp.common.comma_separated_list + "}"
rule.parse_string("{something}")

# >>> ParseException: Expected '}', found end of text

列表中的值也可以包含括号,因此规则必须仅匹配外部值。我猜 [1:-1] 解决了它,但这不是我正在寻找的解决方案。

python pyparsing
2个回答
1
投票

您需要引用以下字符串:

import pyparsing as pp

rule = "{" + pp.common.comma_separated_list + "}"
result = rule.parse_string("{\"something\",\"anything\"}")
print(result)


0
投票

Pyparsing 的

comma_separated_list
是此类列表常见情况的便利助手,但它硬编码了它希望看到的列表元素。还有,你的这条评论:

列表中的值也可以包含括号

告诉我你实际上需要一个递归语法来解析 {} 中的 {}。

因此,与其使用

comma_separated_list
帮助程序(或其类似的帮助程序
nested_expr
),我将分步介绍这个表达式的定义。 (我们使用
delimited_list
,因为它允许我们为列表的元素定义一个表达式。)

编写新语法时我采取的第一步是为将要解析的标点符号定义常量。通常,标点符号在解析时很重要,但通常可以从实际解析结果中删除。

import pyparsing as pp

LBRACE, RBRACE = map(pp.Suppress, "{}")
# in pyparsing 3.1.0, you will be able to write this as
# LBRACE, RBRACE = Suppress.using_each("{}")

由于

rule
将是递归的,我们首先将其定义为 pyparsing
Forward
。也就是说,我们是 forward-declaring 一个表达式,我们稍后会定义它的内容。这允许我们使用
rule
作为它自己定义的一部分。 (我还将对大多数这些表达式调用
set_name
,以便我们可以生成一些漂亮的铁路图。)

rule = pp.Forward().set_name("rule")

将成为规则元素的单词将是任何可打印(即非空格)字符,不包括我们需要用于包含规则和分隔列表的“{”、“}”和“,”字符。

wd_chars = pp.printables
wd =  pp.Word(wd_chars, exclude_chars='{},').set_name("word")

要将

delimited_list
与自定义内容一起使用,我们需要一个 pyparsing 表达式来表示列表中的项目可能是什么。到目前为止,它们要么是
wd
表达式,要么是
rule
表达式。

content = wd | rule

所以现在我们准备好定义

rule
是什么了。我们不能使用普通的“=”运算符,因为那会取代我们用来定义
rule
content
。相反,我们使用“<<=" operator, as a way of "injecting" the grammar for the existing
rule
表达式:

rule <<= pp.Group(LBRACE + pp.delimited_list(content) + RBRACE)

测试解析器的一种简单方法是使用

run_tests
run_tests
回显输入的测试字符串,然后列出已解析的项,或凸起的
ParseException
。事实上,如果你正在关注测试驱动开发,你可以从编写测试代码开始:

rule.run_tests("""
    a simple test
    a slightly more complicated test
    ... etc. ...
""")

然后在您要添加解析器的新方面时继续添加新测试。

run_tests
也接受评论,因此您可以在定义新测试字符串时标记您的测试。以下是按复杂程度递增顺序排列的一些测试:

rule.run_tests("""
    # a rule with just one item
    {something}

    # a rule with multiple items
    {thing1, thing2}

    # a rule containing a simple embedded rule
    {thing1, {thing2a}, thing3}
    
    # a rule containing a rule with multiple items
    {thing1, {thing2a, thing2b}, thing3}
    
    # an empty rule
    {}
""", full_dump=False)

打印:

# a rule with just one item
{something}
[['something']]

# a rule with multiple items
{thing1, thing2}
[['thing1', 'thing2']]

# a rule containing a simple embedded rule
{thing1, {thing2a}, thing3}
[['thing1', ['thing2a'], 'thing3']]

# a rule containing a rule with multiple items
{thing1, {thing2a, thing2b}, thing3}
[['thing1', ['thing2a', 'thing2b'], 'thing3']]

# an empty rule
{}
{}
 ^
ParseException: Expected rule, found '}'  (at char 1), (line:1, col:2)
FAIL: Expected rule, found '}'  (at char 1), (line:1, col:2)

有趣!如果不支持空规则,则只需删除最后一个测试。但是如果您希望能够解析这样的规则,那么我们需要稍微修改我们的解析器,以表明 {} 中的内容项列表是可选的:

rule <<= pp.Group(LBRACE + pp.Opt(pp.delimited_list(content)) + RBRACE)
# in pyparsing 3.1.0, you will be able to write this as
# rule <<= pp.Group(LBRACE + (pp.delimited_list(content) | "") + RBRACE)

这是 pyparsing 将为此解析器创建的铁路图,使用以下语句:

rule.create_diagram("rule_parser.html")

最后,如果我们需要创建一个包含逗号或大括号的规则项怎么办?或者可能是多个单词?期望这样的项目应该用引号括起来并不是不合理的。 Pyparsing 包括一些内置表达式,例如

dbl_quoted_string
表示包含在
"
字符中的字符串。如果我们需要,我们可以扩展
content
的定义:

content = pp.dbl_quoted_string | wd | rule

修改了

content
的图表说明:

(一个读者学习的问题:为什么

dbl_quoted_string
表达式必须在
wd
之前?)

并将此测试添加到我们对

run_tests
的调用中:

# a rule with embedded , and brace characters
{thing1, "tricky rule with characters like , { and }", thing2}

这增加了输出:

# a rule with embedded , and brace characters
{thing1, "tricky rule with characters like , { and }", thing2}
[['thing1', '"tricky rule with characters like , { and }"', 'thing2']]

我可能对你试图用你的解析器做什么做了一些不正确的假设,但希望从这个例子中你可以继续使用你自己的规则解析器版本。

© www.soinside.com 2019 - 2024. All rights reserved.