5

蟒蛇爱好者:

假设您想使用 Pyparsing 解析以下字符串:

'ABC_123_SPEED_X 123'

wereABC_123是一个标识符;SPEED_X是一个参数,123是一个值。我想到了使用 Pyparsing 的以下 BNF:

Identifier = Word( alphanums + '_' )
Parameter = Keyword('SPEED_X') or Keyword('SPEED_Y') or Keyword('SPEED_Z')
Value = # assume I already have an expression valid for any value
Entry = Identifier + Literal('_') + Parameter + Value
tokens = Entry.parseString('ABC_123_SPEED_X 123')
#Error: pyparsing.ParseException: Expected "_" (at char 16), (line:1, col:17)

如果我从中间删除下划线(并Entry相应地调整定义)它会正确解析。

我怎样才能让这个解析器变得更懒一点,然后等到它与关键字匹配(而不是将整个字符串作为标识符并等待_不存在的 .

谢谢你。

[注意:这是对我的问题的完全重写;我还没有意识到真正的问题是什么]

4

4 回答 4

7

我的答案是基于这个,因为你想要做的是得到一个非贪婪的匹配。看起来这在 pyparsing 中很难实现,但通过一些聪明和妥协​​并非不可能。以下似乎有效:

from pyparsing import *
Parameter = Literal('SPEED_X') | Literal('SPEED_Y') | Literal('SPEED_Z')
UndParam = Suppress('_') + Parameter
Identifier = SkipTo(UndParam)
Value = Word(nums)
Entry = Identifier + UndParam + Value

当我们从交互式解释器运行它时,我们可以看到以下内容:

>>> Entry.parseString('ABC_123_SPEED_X 123')
(['ABC_123', 'SPEED_X', '123'], {})

请注意,这是一种妥协;因为我使用SkipToIdentifier可以充满邪恶,令人作呕的字符,而不仅仅是alphanums偶尔的下划线。

编辑:感谢 Paul McGuire,我们可以通过设置Identifier以下内容来炮制一个真正优雅的解决方案:

Identifier = Combine(Word(alphanums) +
        ZeroOrMore('_' + ~Parameter + Word(alphanums)))

让我们检查一下这是如何工作的。首先,忽略外部Combine;我们稍后再谈。从Word(alphanums)我们知道我们将得到'ABC'引用字符串的一部分开始,'ABC_123_SPEED_X 123'. 需要注意的是,在这种情况下,我们不允许“单词”包含下划线。我们将其单独构建到逻辑中。

接下来,我们需要在'_123'不吸入的情况下捕获部分'_SPEED_X'。让我们也跳过ZeroOrMore这一点,稍后再返回。我们将下划线作为 . 开头Literal,但我们可以使用 just 进行快捷方式'_',这将使我们成为前导下划线,但不是全部'_123'。本能地,我们会放置另一个Word(alphanums)来捕获其余的,但这正是消耗所有剩余的给我们带来麻烦的原因'_123_SPEED_X'。相反,我们说,“只要下划线后面的不是,就将其Parameter解析为 my 的一部分Identifier。我们在 pyparsing 术语中将其声明为'_' + ~Parameter + Word(alphanums)。由于我们假设我们可以有任意数量的下划线 + WordButNotParameter 重复,我们将其包装起来表达式 aZeroOrMore构造。(如果您总是希望在首字母之后至少有下划线 + WordButNotParameter,您可以使用OneOrMore.)

最后,我们需要将初始 Word 和特殊的下划线 + Word 重复包装在一起,以便理解它们是连续的,而不是由空格分隔,因此我们将整个表达式包装在一个Combine构造中。这种方式'ABC _123_SPEED_X'会引发解析错误,但'ABC_123_SPEED_X'会正确解析。

另请注意,我必须更改Keyword为,Literal因为前者的方式太微妙且容易生气。我不信任Keywords,也无法与他们匹配。

于 2009-12-15T07:46:51.887 回答
1

如果您确定标识符永远不会以下划线结尾,您可以在定义中强制执行它:

from pyparsing import *

my_string = 'ABC_123_SPEED_X 123'

Identifier = Combine(Word(alphanums) + Literal('_') + Word(alphanums))
Parameter = Literal('SPEED_X') | Literal('SPEED_Y') | Literal('SPEED_Z')
Value = Word(nums)
Entry = Identifier + Literal('_').suppress() + Parameter  + Value
tokens = Entry.parseString(my_string)

print tokens # prints: ['ABC_123', 'SPEED_X', '123']

如果不是这种情况,但如果标识符长度是固定的,您可以像这样定义标识符:

Identifier = Word( alphanums + '_' , exact=7)
于 2009-12-15T09:55:02.577 回答
1

您还可以将标识符和参数解析为一个标记,并在解析操作中将它们拆分:

from pyparsing import *
import re

def split_ident_and_param(tokens):
    mo = re.match(r"^(.*?_.*?)_(.*?_.*?)$", tokens[0])
    return [mo.group(1), mo.group(2)]

ident_and_param = Word(alphanums + "_").setParseAction(split_ident_and_param)
value = Word(nums)
entry = ident_and_param + value

print entry.parseString("APC_123_SPEED_X 123")

上面的示例假定标识符和参数始终具有 XXX_YYY 格式(包含一个下划线)。

如果不是这种情况,则需要调整split_ident_and_param()方法。

于 2009-12-15T10:31:12.373 回答
-1

这回答了您可能也问过自己的一个问题:“什么是现实世界的应用程序reduce?):

>>> keys = ['CAT', 'DOG', 'HORSE', 'DEER', 'RHINOCEROS']
>>> p = reduce(lambda x, y: x | y, [Keyword(x) for x in keys])
>>> p
{{{{"CAT" | "DOG"} | "HORSE"} | "DEER"} | "RHINOCEROS"}

编辑:

这是对原始问题的一个很好的答案。我将不得不为新的工作。

进一步编辑:

我很确定你不能做你想做的事。创建的解析器pyparsing不进行前瞻。因此,如果您告诉它 match Word(alphanums + '_'),它将继续匹配字符,直到找到不是字母、数字或下划线的字符。

于 2009-12-15T06:31:42.813 回答