2014-10-28 86 views
4

我试图使用pyparsing来分析robotframework,这是一个基于文本的DSL。 sytnax就像下面一样(对不起,但我认为在BNF中描述它有点困难)。 在robotframework单个线可以是这样的:pyparsing空白匹配问题

Library\tSSHClient with name\tnode 

\ t是标签,并在robotframework,它被透明转移到2“,“(实际上,它只需调用str.replace( '\ t' 的'')替换标签,但它会修改每行的实际长度,len('\ t')是1,但len('')是2)。 在机器人中,如果单词之间只有1个空格,则会使用2个或多个空格和'\ t'来分割令牌,则这些单词将被视为令牌组。

Library\tSSHClient with name\tnode 

实际上分裂为以下令牌如果解析正确:

['Library', 'SSHClient', 'with name', 'node'] 

由于只有1之间的“与”和“名”的空白,所以解析器认为属于一组语法令牌。

这里是我的代码:

ParserElement.setDefaultWhitespaceChars('\r\n\t ') 
source = "Library\tSSHClient with name\tnode" 
EACH_LINE = Optional(Word(" ")).leaveWhitespace().suppress() + \ 
      CaselessKeyword("library").suppress() + \ 
      OneOrMore((Word(alphas)) + White(max=1).setResultName('myValue')) +\ 
      SkipTo(LineEnd()) 

res = EACH_LINE.parseString(source) 
print res.myValue 

问题:

1)我已经设置了空格,如果我想精确匹配2个或多个空格和一个或多个标签,我想代码想要: 白色(ws ='',min = 2)|白色(ws ='\ t',min = 1) 但这会失败,所以我无法指定空白值?

2)有没有办法获得匹配的结果索引?我尝试了setParseAction,但似乎我无法通过此回调获取索引。我需要开始和结束索引来突出这个词。

3)LineStart和LineEnd的含义是什么?我打印这些值,似乎他们只是正常的字符串,我是否必须在行的前面写一些东西: LineStart()+ balabala ... + LineEnd()?

感谢,但是,没有,我不能代替 '\ t' 到 ''

from pyparsing import * 

source = "Library\tsshclient\t\t\twith name s1" 

value = Combine(OneOrMore(Word(printables) | White(' ', max=1) + ~White())) #here it seems the whitespace has already been set to ' ', why the result still match '\t'? 

linedefn = OneOrMore(value) 

res = linedefn.parseString(source) 

print res 

['Library sshclient', 'with name', 's1'] 

,但我预计 [ '图书馆' 的限制,'sshclient','with name','s1']

回答

5

当空白陷入解析标记时,我总是退缩,但是随着您的约束只有一个空格是允许的,这应该是可行的。我用下面的表达式来定义,可能有嵌入一个空格你的价值观:

# each value consists of printable words separated by at most a 
# single space (a space that is not followed by another space) 
value = Combine(OneOrMore(Word(printables) | White(' ',max=1) + ~White())) 

完成这一操作,行仅仅是其中一个或多个值:按照您的例子

linedefn = OneOrMore(value) 

,包括调用str。替换双空格替换选项卡,代码如下:

data = "Library\tSSHClient with name\tnode" 

# replace tabs with 2 spaces 
data = data.replace('\t', ' ') 

print linedefn.parseString(data) 

,并提供:

['Library', 'SSHClient', 'with name', 'node'] 

要获取原始字符串的任何值的起点和终点的位置,包裹在表达新pyparsing helper方法locatedExpr

# use new locatedExpr to get the value, start, and end location 
# for each value 
linedefn = OneOrMore(locatedExpr(value))('values') 

如果我们分析和结果转储:

print linedefn.parseString(data).dump() 

我们得到:

- values: 
    [0]: 
    [0, 'Library', 7] 
    - locn_end: 7 
    - locn_start: 0 
    - value: Library 
    [1]: 
    [9, 'SSHClient', 18] 
    - locn_end: 18 
    - locn_start: 9 
    - value: SSHClient 
    [2]: 
    [22, 'with name', 31] 
    - locn_end: 31 
    - locn_start: 22 
    - value: with name 
    [3]: 
    [33, 'node', 37] 
    - locn_end: 37 
    - locn_start: 33 
    - value: node 

LineStart和LineEnd被pyparsing表达类的实例应该匹配一行的开始和结束。 LineStart一直很难合作,但LineEnd相当可预测。就你而言,如果你只是一次阅读和解析一行,那么你就不需要它们 - 只需定义你所期望的行的内容即可。如果要确保解析器已处理完整个字符串(并且由于不匹配的字符而未停止搜索),请将+ LineEnd()+ StringEnd()添加到解析器的末尾,或将参数parseAll=True添加到您的调用中parseString()

编辑:

人们很容易忘记,pyparsing默认调用str.expandtabs - 你必须通过调用parseWithTabs禁用此。那么,明确地禁止价值词之间的TAB可以解决您的问题,并将值保留在正确的字符数上。见下面的变化:

from pyparsing import * 
TAB = White('\t') 

# each value consists of printable words separated by at most a 
# single space (a space that is not followed by another space) 
value = Combine(OneOrMore(~TAB + (Word(printables) | White(' ',max=1) + ~White()))) 

# each line has one or more of these values 
linedefn = OneOrMore(value) 
# do not expand tabs before parsing 
linedefn.parseWithTabs() 


data = "Library\tSSHClient with name\tnode" 

# replace tabs with 2 spaces 
#data = data.replace('\t', ' ') 

print linedefn.parseString(data) 


linedefn = OneOrMore(locatedExpr(value))('values') 
# do not expand tabs before parsing 
linedefn.parseWithTabs() 
print linedefn.parseString(data).dump() 
+0

保罗麦圭尔,这是我的荣幸,让你回答我的问题,非常感谢。也许我没有让自己清楚,DSL语法是“2个或更多个空格”或者“1个或多个标签”用作语法标记分析器。所以我不能用''替换'tab',否则'\ t'将被平等地视为空白。从pyparsing导入* 我已经更新了最后的问题,请帮忙看看。 – python 2014-10-28 06:09:00

+0

事实上,我试图突出显示DSL令牌,因此我需要准确获取eacth令牌的开始和结束索引,每个令牌之间可以有“1个或多个选项卡”或“2个或更多个空格”,如果我替换''t'to'',这个解析过程会使行的长度发生变化(len('\ t')= 1,但len('')= 2),所以我需要解析行而不用字符串替换。 – python 2014-10-28 06:22:14

+0

谢谢,pyparsing似乎是最优雅的解决方案,非常感谢 – python 2014-10-29 03:22:09