Pyparsing中的贪婪表达

时间:2014-11-22 20:26:15

标签: python pyparsing

我尝试使用Pyparsing将aaa:bbb(123)之类的字符串拆分为令牌。

我可以使用正则表达式执行此操作,但我需要通过Pyparsing来完成。

使用re解决方案将如下所示:

>>> import re
>>> string = 'aaa:bbb(123)'
>>> regex = '(\S+):(\S+)\((\d+)\)'
>>> re.match(regex, string).groups()
('aaa', 'bbb', '123')

这很简单。这里的关键点是\S+,这意味着"除了空格之外的所有内容"。

现在我将尝试使用Pyparsing:

>>> from pyparsing import Word, Suppress, nums, printables
>>> expr = (
...     Word(printables, excludeChars=':')
...     + Suppress(':')
...     + Word(printables, excludeChars='(')
...     + Suppress('(')
...     + Word(nums)
...     + Suppress(')')
... )
>>> expr.parseString(string).asList()
['aaa', 'bbb', '123']

好的,我们得到了相同的结果,但这看起来并不好。我们已设置excludeChars以使Pyparsing表达式停在我们需要的位置,但这看起来并不健全。如果我们将"排除"源字符串中的字符,相同的正则表达式将正常工作:

>>> string = 'a:aa:b(bb(123)'
>>> re.match(regex, string).groups()
('a:aa', 'b(bb', '123')

而Pyparsing异常显然会破坏:

>>> expr.parseString(string).asList()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "/long/path/to/pyparsing.py", line 1111, in parseString
    raise exc
ParseException: Expected W:(0123...) (at char 7), (line:1, col:8)

那么,问题是我们如何通过Pyparsing实现所需的逻辑?

2 个答案:

答案 0 :(得分:2)

与正则表达式不同,pyparsing纯粹是从左到右的搜索,没有隐含的前瞻。

如果您想要正则表达式的前瞻和回溯,您可以使用包含原始版本的正则表达式:

expr = Regex(r"(\S+):(\S+)\((\d+)\)")
print expr.parseString(string).dump()

['aaa:b(bb(123)']

但是,我发现这只会将整个匹配作为单个字符串返回。如果您希望能够访问各个组,则必须将它们定义为命名组:

expr = Regex(r"(?P<field1>\S+):(?P<field2>\S+)\((?P<field3>\d+)\)")
print expr.parseString(string).dump()

['aaa:b(bb(123)']
- field1: aaa
- field2: b(bb
- field3: 123    

这告诉我,一个好的增强功能是将一个构造函数arg添加到Regex,以将结果作为所有re组的列表而不是字符串返回。

答案 1 :(得分:1)

使用具有先行断言的正则表达式:

from pyparsing import Word, Suppress, Regex, nums, printables

expr = (
     Word(printables, excludeChars=':')
     + Suppress(':')
     + Regex(r'\S+[^\(](?=\()')
     + Suppress('(')
     + Word(nums)
     + Suppress(')')
 )