【问题标题】:How exactly do the lazy quantifiers work in PCRE?惰性量词在 PCRE 中究竟是如何工作的?
【发布时间】:2014-01-16 08:39:40
【问题描述】:

一些背景知识:我正在实现一个正则表达式匹配引擎 (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 的回溯特定行为的建议很可能是错误的......

【问题讨论】:

  • 我曾经犯过一个错误,就是看看它是如何完成的。 SCARY! 据我所知,完整的 PCRE 需要堆栈评估模型,而不是自动机理论模型。与基于自动机的方法最接近的可能是 PostgreSQL 和 Tcl 中使用的 RE 引擎,但这真的很复杂。
  • 感谢您的快速回复。但这似乎不是回溯的特定行为(请参阅编辑),并且可以使用自动机实现(尽管我知道 TRE 未能通过 AT&T 测试数据存储库的一些 POSIX 合规性测试,因此,这种行为可能是一种“错误")。

标签: regex nfa


【解决方案1】:

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

阅读这本书中的定义Regular Expressions Cookbook

(?(1)then|else) 是一个条件,检查第一个捕获组是否 已经匹配了一些东西。如果有,正则表达式引擎会尝试匹配 然后。如果捕获组到目前为止还没有参加比赛尝试, 尝试 else 部分。

  • 这个主题:&lt;a href="abcd xyz pqr" cats

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

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

  • 这个主题:&lt;a href="abcd

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

    REGEX 引擎不会在这里停止,你已经使用了([\"\'])? 所以,引擎可能会再试一次,因为" 字符是可选的,并且它会继续运行,就好像第一个捕获组没有匹配(确实有回溯)。因此,现在引擎达到了第一个捕获组不匹配的条件,尝试 else 部分并设法匹配字符串"abcd" 字符由于回溯而与第一个捕获组不匹配现在它与 else 部分中的第三个捕获组匹配)正则表达式可能会成功完成。

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

【讨论】:

    【解决方案2】:

    我在这里不完全理解您的问题,但非贪婪量词允许搜索模式的第一次出现。使用 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-23
      • 2019-07-09
      • 1970-01-01
      • 1970-01-01
      • 2023-04-01
      • 1970-01-01
      • 2011-06-26
      相关资源
      最近更新 更多