如何正确使用LPeg进行预查。

为了匹配以dog开头,紧随其后为cat(但不包括消耗cat)的字符串,可以使用以下代码:

local lpeg = require 'lpeg'
local str1 = 'dogcat'
local patt1 = lpeg.C(lpeg.P('dog')) * #lpeg.P('cat')
print(lpeg.match(patt1, str1))

输出为:dog

要匹配以dog开头,然后可接任何字符序列,最后跟随cat(但不包括消耗它),类似于正则表达式前视(dog.+?)(?=cat),我尝试了以下代码:

local str2 = 'dog and cat'
local patt2 = lpeg.C(lpeg.P("dog") * lpeg.P(1) ^ 1) * #lpeg.P("cat")
print(lpeg.match(patt2, str2))

我期望的结果是dog and,但它返回了nil

如果我放弃前视部分(即使用模式lpeg.C(lpeg.P(“dog”)* lpeg.P(1)^ 1)),它可以成功匹配整个字符串。这意味着* lpeg.P(1)^ 1部分正确地匹配了任何字符序列,不是吗?

如何修复它?

点赞
用户1442917
用户1442917

你需要在预读中的每个可以匹配的位置否定"cat":

local patt2 = lpeg.C(lpeg.P"dog" * (lpeg.P(1)-lpeg.P"cat") ^ 1) * #lpeg.P"cat"

我想把我一直在工作的调试器([pegdebug](http://github.com/pkulchenko/pegdebug))插入,因为它可以帮助像这样的情况。下面是它为原始lpeg表达式生成的输出:

+   Exp 1   "d"
 +  Dog 1   "d"
 =  Dog 1-3 "dog"
 +  Separator   4   " "
 =  Separator   4-11    " and cat"
 +  Cat 12  ""
 -  Cat 12
-   Exp 1

您可以看到,Separator表达式“吃掉”了所有字符,包括“cat”,因此没有任何东西可以与P"cat"匹配。

经过修改的表达式的输出如下所示:

+   Exp 1   "d"
 +  Dog 1   "d"
 =  Dog 1-3 "dog"
 +  Separator   4   " "
 =  Separator   4-8 " and "
 +  Cat 9   "c"
 =  Cat 9-11    "cat"
=   Exp 1-8 "dog and "
/   Dog 1   0
/   Separator   4   0
/   Exp 1   1   "dog and "

这是完整的脚本:

require 'lpeg'
local peg = require 'pegdebug'
local str2 = 'dog and cat'
local patt2 = lpeg.P(peg.trace { "Exp";
  Exp = lpeg.C(lpeg.V"Dog" * lpeg.V"Separator") * #lpeg.V"Cat";
  Cat = lpeg.P("cat");
  Dog = lpeg.P("dog");
  Separator = (lpeg.P(1) - lpeg.P("cat"))^1;
})
print(lpeg.match(patt2, str2))
2014-11-19 06:31:57