懒惰量词在PCRE中的作用究竟如何?

时间:2014-01-16 08:39:40

标签: regex nfa

一些背景知识:我正在实现一个正则表达式匹配引擎(NFA),它应该支持PCRE兼容模式(我的意思是它应该捕获具有与PCRE相同的偏移量的子表达式)。

在PCRE的testinput1中有一个测试我无法完全理解。它测试了懒惰的量词。

所以,正则表达式是

/<a[\s]+href[\s]*=[\s]*          # find <a href=
 ([\"\'])?                       # find single or double quote
 (?(1) (.*?)\1 | ([^\s]+))       # if quote found, match up to next matching
                                 # quote, otherwise match up to next space
/isx

字符串是

<a href="abcd xyz pqr" cats

PCRE的匹配是:

<a href="abcd xyz pqr"

显然使用了懒惰的量词。

据我所知,懒惰的量词不应该被使用,直到另一种“贪婪”的方式完全不可能。现在这是一场可能的贪婪比赛:

<a href="abcd

使用条件子模式的负分支,没有惰性量词。

所以我正在寻找对此PCRE行为的解释或任何细节/建议,为什么懒惰量词在此测试中匹配。谢谢!

编辑:我还查看了TRE库的工作原理。它是POSIX兼容的NFA引擎。我修改了原始正则表达式以适应TRE的语法:

#include <stdlib.h>
#include <stdio.h>
#include <tre/tre.h>

int main()
{
    regex_t preg;
    const char * regex = "<a[ ]+href[ ]*=[ ]*(?:(')(.*?)'|[^ ]+)";
    const char * string = "<a href='abcd xyz pqr' cats";
    int cflags = REG_EXTENDED;
    int eflags = 0;
    size_t nmatch = 3;
    regmatch_t pmatch[100];

    tre_regcomp(&preg, regex, cflags);
    tre_regexec(&preg, string, nmatch, pmatch, eflags);

    for (int i = 0; i < nmatch; i++) {
        printf("%d: (%d, %d)\n", i, pmatch[i].rm_so, pmatch[i].rm_eo - pmatch[i].rm_so);
    }

    return 0;
}

和输出(使用长度而不是结束偏移)是:

0: (0, 22)
1: (8, 1)
2: (9, 12)

因此关于PCRE特定回溯行为的建议很可能是错误的......

2 个答案:

答案 0 :(得分:1)

首先,我只是REGEX世界的初学者。所以,如果这个答案错了​​或我误解了这个问题,我很抱歉。

阅读本书Regular Expressions Cookbook

中的这个定义
  

(?(1)则| else)是检查第一个捕获组是否为条件的条件     已经匹配了一些东西。如果有,则正则表达式引擎尝试匹配     然后。如果捕获组尚未参与到目前为止的匹配尝试,     尝试了其他部分。

  • 使用此主题:<a href="abcd xyz pqr" cats

    第一个捕获组已匹配第一个"字符。因此,预期的行为是尝试匹配当时的部分。 then部分中的第二个捕获组设法将字符串abcd xyz pqr(.*?)匹配,最后部分设法将abcd xyz pqr"(.*?)\1匹配。 REGEX可能会成功完成。

    因此,不需要带有greddy量词的else部分,实际上它没有被使用。好像greddy量词从未存在过。

  • 使用此主题:<a href="abcd

    第一个捕获组已匹配"个字符。现在,then部分设法将字符串abcd(.*?)匹配,但它永远不会匹配最后一个"字符,因为主题末尾没有这样的字符。条件失败。

    REGEX引擎不会在此处停止,您已使用([\"\'])?因此,引擎可能会再次尝试,因为"字符是可选的,并且它会一直继续,就像第一个捕获组不匹配一样(确实有回溯)。因此,现在引擎达到条件而没有匹配第一个捕获组,尝试使用else部分并且它设法匹配字符串"abcd"字符与第一个捕获组不匹配因为回溯现在它与其他部分中的第三个捕获组相匹配)REGEX可能会成功完成。

PS:我正在学习有关正则表达式的有趣内容,所以这个答案可能是完全错误的。等待更好的答案。

答案 1 :(得分:0)

我在这里并不完全理解你的问题,但非贪婪量词允许搜索第一次出现的模式。使用pcretest,您可以在同一输入上尝试贪婪和非贪婪的形式。

非贪婪形式:

  re> /<a[\s]+href[\s]*=[\s]*([\"\'])?(?(1)(.*?)\1|([^\s]+))/i
  data> <a href="ab"cd"
    0: <a href="ab"
    1: "
    2: ab

贪婪的形式:

 re> /<a[\s]+href[\s]*=[\s]*([\"\'])?(?(1)(.*)\1|([^\s]+))/i
 data> <a href="ab"cd"
    0: <a href="ab"cd"
    1: "
    2: ab"cd