pyparsing多行可选的结果集中缺少数据

时间:2013-05-18 13:12:51

标签: pyparsing

我是一个非常新的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缺少服务标签???

这里有什么不对。

先谢谢 劳伦

1 个答案:

答案 0 :(得分:3)

你看到的问题是,在你的表达式中,只有在跳过可选的SVC和SRC之后才会查找DST。你有几个选择,我会仔细阅读每个选项,这样你就可以了解这里发生了什么。

(但首先,编写“Optional(ZeroOrMore(anything))”没有意义 - ZeroOrMore已经暗示可选,所以我将在任何这些选择中删除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*"))

注意每个名称上的尾部'*' - 这相当于调用setRllultsName并且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,但不能接受。 (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似乎让人感到困惑,可能仍然是如何做到这一点的错误。所以你可能不得不保留使用每个更基本的情况,比如我的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)

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