3

我是一个全新的 pyparsing 用户,并且缺少匹配项,我不明白:

这是我要解析的文本:

polraw="""
set policy id 800 from "Untrust" to "Trust"  "IP_10.124.10.6" "MIP(10.0.2.175)" "TCP_1002" permit
set policy id 800
set dst-address "MIP(10.0.2.188)"
set service "TCP_1002-1005"
set log session-init
exit
set policy id 724 from "Trust" to "Untrust"  "IP_10.16.14.28" "IP_10.24.10.6" "TCP_1002" permit
set policy id 724
set src-address "IP_10.162.14.38"
set dst-address "IP_10.3.28.38"
set service "TCP_1002-1005"
set log session-init
exit
set policy id 233 name "THE NAME is 527 ;" from "Untrust" to "Trust"  "IP_10.24.108.6" "MIP(10.0.2.149)" "TCP_1002" permit
set policy id 233
set service "TCP_1002-1005"
set service "TCP_1006-1008"
set service "TCP_1786"
set log session-init
exit

"""

我这样设置语法:

KPOL  = Suppress(Keyword('set policy id'))
NUM   = Regex(r'\d+')
KSVC  = Suppress(Keyword('set service'))
KSRC  = Suppress(Keyword('set src-address'))
KDST  = Suppress(Keyword('set dst-address'))
SVC    = dblQuotedString.setParseAction(lambda t: t[0].replace('"',''))
ADDR   = dblQuotedString.setParseAction(lambda t: t[0].replace('"',''))
EXIT  = Suppress(Keyword('exit'))
EOL = LineEnd().suppress()

P_SVC = KSVC + SVC + EOL
P_SRC = KSRC + ADDR + EOL
P_DST = KDST + ADDR + EOL

x = KPOL + NUM('PId') + EOL + Optional(ZeroOrMore(P_SVC)) + Optional(ZeroOrMore(P_SRC)) + Optional(ZeroOrMore(P_DST)) 

for z in x.searchString(polraw):
    print z

结果集如

['800', 'MIP(10.0.2.188)']
['724', 'IP_10.162.14.38', 'IP_10.3.28.38']
['233', 'TCP_1002-1005', 'TCP_1006-1008', 'TCP_1786']

800 缺少服务标签???

这里有什么问题。

提前感谢洛朗

4

1 回答 1

3

您看到的问题是,在您的表达式中,仅在跳过可选的 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)

现在,您的解析器将干净地处理表达式中各个关键字之间的额外空格(甚至是注释!)。

于 2013-05-19T06:32:11.243 回答