您看到的问题是,在您的表达式中,仅在跳过可选的 SVC 和 SRC 之后才查找 DST。您有几个选项,我将逐一介绍,以便您了解这里发生的一切。
(但首先,写“Optional(ZeroOrMore(anything))”是没有意义的——ZeroOrMore 已经暗示了 Optional,所以我将在任何这些选择中删除 Optional 部分。)
如果要以任意顺序获取 SVC、SRC 和 DST,则可以重构 ZeroOrMore 以接受这三种数据类型中的任何一种,如下所示:
x = KPOL + NUM('PId') + EOL + ZeroOrMore(P_SVC|P_SRC|P_DST)
这将允许您混合不同类型的语句,并且它们都将作为 ZeroOrMore 重复的一部分被收集。
如果要将这些不同类型的语句分组,则可以为每个语句添加一个结果名称:
x = KPOL + NUM('PId') + EOL + ZeroOrMore(P_SVC("svc*")|
P_SRC("src*")|
P_DST("dst*"))
请注意每个名称后面的“*” - 这相当于调用 setResultsName 且 listAllMatches 参数等于 True。随着每个不同的表达式匹配,不同类型的结果将被收集到“svc”、“src”或“dst”结果名称中。调用z.dump()
将列出标记和结果名称及其值,因此您可以看到它是如何工作的。
set policy id 233
set service "TCP_1002-1005"
set dst-address "IP_10.3.28.38"
set service "TCP_1006-1008"
set service "TCP_1786"
set log session-init
exit
显示这个z.dump()
:
['233', 'TCP_1002-1005', 'IP_10.3.28.38', 'TCP_1006-1008', 'TCP_1786']
- PId: 233
- dst: [['IP_10.3.28.38']]
- svc: [['TCP_1002-1005'], ['TCP_1006-1008'], ['TCP_1786']]
如果你在 P_xxx 表达式上包装 ungroup,可能是这样的:
P_SVC,P_SRC,P_DST = (ungroup(expr) for expr in (P_SVC,P_SRC,P_DST))
那么输出看起来更干净:
['233', 'TCP_1002-1005', 'IP_10.3.28.38', 'TCP_1006-1008', 'TCP_1786']
- PId: 233
- dst: ['IP_10.3.28.38']
- svc: ['TCP_1002-1005', 'TCP_1006-1008', 'TCP_1786']
这实际上看起来不错,但让我传递另一种选择。在许多情况下,解析器必须以任意顺序查找多个子表达式。假设它们是 A、B、C 和 D。要以任何顺序接受这些,您可以编写类似 的内容OneOrMore(A|B|C|D)
,但这将接受多个 A,或 A、B 和 C,但不接受 D。穷举/穷举组合(A+B+C+D) 的爆炸 | (A+B+D+C) | 等可以写出来,或者你可以用类似的东西自动化它
from itertools import permutations
mixNmatch = MatchFirst(And(p) for p in permutations((A,B,C,D),4))
但是 pyparsing 中有一个名为 Each 的类,它允许编写相同的东西:
Each([A,B,C,D])
意思是“A、B、C 和 D 中必须各有一个,顺序不限”。和 And、Or、NotAny 等一样,也有一个操作符快捷方式:
A & B & C & D
这意味着同样的事情。
如果你想要“必须有 A、B 和 C,以及可选的 D”,那么写:
A & B & C & Optional(D)
这将以相同的行为进行解析,查找 A、B、C 和 D,无论传入顺序如何,以及 D 是最后还是与 A、B 和 C 混合。您还可以使用 OneOrMore 和ZeroOrMore 表示任何表达式的可选重复。
所以你可以把你的表达式写成:
x = KPOL + NUM('PId') + EOL + (ZeroOrMore(P_SVC) &
ZeroOrMore(P_SRC) &
ZeroOrMore(P_DST))
我查看了使用此表达式的结果名称,ZeroOrMore 似乎令人困惑,也许仍然是如何完成的错误。因此,您可能必须使用 Each 来保留更基本的情况,例如我的 A、B、C、D 示例。但我想让你意识到这一点。
关于解析器的其他一些注释:
dblQuotedString.setParseAction(lambda t: t[0].replace('"',''))
可能写得更好
dblQuotedString.setParseAction(removeQuotes)
。您的示例中没有任何嵌入的引号,但最好知道您的假设可能不会转化为未来的应用程序。以下是删除定义引号的几种方法:
dblQuotedString.setParseAction(lambda t: t[0].replace('"',''))
print dblQuotedString.parseString(r'"This is an embedded quote \" and an ending quote \""')[0]
# prints 'This is an embedded quote \ and an ending quote \'
# removed leading and trailing "s, but also internal ones too, which are
# really part of the quoted string
dblQuotedString.setParseAction(lambda t: t[0].strip('"'))
print dblQuotedString.parseString(r'"This is an embedded quote \" and an ending quote \""')[0]
# prints 'This is an embedded quote \" and an ending quote \'
# removed leading and trailing "s, and leaves the one internal ones but strips off
# the escaped ending quote
dblQuotedString.setParseAction(removeQuotes)
print dblQuotedString.parseString(r'"This is an embedded quote \" and an ending quote \""')[0]
# prints 'This is an embedded quote \" and an ending quote \"'
# just removes leading and trailing " characters, leaves escaped "s in place
KPOL = Suppress(Keyword('set policy id'))
有点脆弱,因为如果 'set' 和 'policy' 之间或 'policy' 和 'id' 之间有任何额外的空格,它会中断。我通常通过首先单独定义所有关键字来定义这些类型的表达式:
SET,POLICY,ID,SERVICE,SRC_ADDRESS,DST_ADDRESS,EXIT = map(Keyword,
"set policy id service src-address dst-address exit".split())
然后使用以下方法定义单独的表达式:
KSVC = Suppress(SET + SERVICE)
KSRC = Suppress(SET + SRC_ADDRESS)
KDST = Suppress(SET + DST_ADDRESS)
现在,您的解析器将干净地处理表达式中各个关键字之间的额外空格(甚至是注释!)。