在PCRE2或支持前向反向引用的任何其他正则表达式引擎中,是否可以将在先前循环迭代中匹配的捕获组更改为非参与捕获组(也称为未设置捕获组或未捕获组),导致测试该组的条件与其"假"条款相匹配而不是它们的"真实"条款?
例如,采用以下PCRE正则表达式:
^(?:(z)?(?(1)aa|a)){2}
Run Code Online (Sandbox Code Playgroud)
当输入字符串时zaazaa,它根据需要匹配整个字符串.但是当zaaaa我吃饱时,我希望它能够匹配zaaa; 相反,它匹配zaaaa整个字符串.(这只是为了举例说明.当然这个例子可以处理,^(?:zaa|a){2}但不是重点.捕获组擦除的实际用法往往是循环,最常做的远远超过2次迭代.)
这样做的另一种方法,也不能按预期工作:
^(?:(?:z()|())(?:\1aa|\2a)){2}
Run Code Online (Sandbox Code Playgroud)
请注意,当循环"展开"时,这两个都可以正常工作,因为它们不再需要擦除已经进行的捕获:
^(?:(z)?(?(1)aa|a))(?:(z)?(?(2)aa|a))
^(?:(?:z()|())(?:\1aa|\2a))(?:(?:z()|())(?:\3aa|\4a))
Run Code Online (Sandbox Code Playgroud)
因此,不是能够使用最简单的条件形式,而是必须使用更复杂的条件,这只适用于此示例,因为"true"匹配z是非空的:
^(?:(z?)(?(?!.*$\1)aa|a)){2}
Run Code Online (Sandbox Code Playgroud)
或者只使用模拟条件:
^(?:(z?)(?:(?!.*$\1)aa|(?=.*$\1)a)){2}
Run Code Online (Sandbox Code Playgroud)
我已经走遍所有的文件我能找到,而且似乎没有甚至没有提及或这种行为明确描述(即捕获环路内进行通过循环迭代坚持,即使他们失败时,重新拍摄).
它与我的直觉预期不同.我会实现它的方法是,评估捕获组与0次重复将擦除/复位它(这样这可能发生在任何捕获基团与*,?或{0,N}量词),但是跳过它由于内的相同的并行其它替换在上一次迭代中获得捕获的组不会擦除它.因此,如果它们包含至少一个元音,那么这个正则表达式仍将匹配单词:
\b(?:a()|e()|i()|o()|u()|\w)++\1\2\3\4\5\b
Run Code Online (Sandbox Code Playgroud)
但是跳过一个捕获组,因为它位于一个未评估的替代组中,该组使用非零重复进行评估,嵌套在上一次迭代期间捕获组占用一个值的组中,将删除/取消设置它,所以这个正则表达式能够\1在循环的每次迭代中捕获或擦除组:
^(?:(?=a|(b)).(?(1)_))*$
Run Code Online (Sandbox Code Playgroud)
并匹配字符串,如aaab_ab_b_aaaab_ab_aab_b_b_aaa.但是,前向引用的方式实际上是在现有引擎中实现的,它匹配aaaaab_a_b_a_a_b_b_a_b_b_b_.
我想知道这个问题的答案,不仅因为它对构建正则表达式很有用,而且因为我编写了自己的正则表达式引擎,目前ECMAScript兼容一些可选的扩展(包括分子前瞻(?*),即非原子前瞻,据我所知,没有其他引擎有),我想继续添加其他引擎的功能,包括前向/嵌套后向引用.我不仅希望我的前向反向引用的实现与现有实现兼容,而且如果没有一种方法可以在其他引擎中擦除捕获组,我可能会在我的引擎中创建一种不冲突的方法与其他现有的正则表达式功能.
需要明确的是:只要有足够的研究和/或引用资料来支持,任何主流引擎都无法做到这一点的答案是可以接受的.的回答说,它是可能的将是国家要容易得多,因为它需要一个例子.
关于非参与捕获组是什么的一些信息:
http://blog.stevenlevithan.com/archives/npcg-javascript-这篇文章最初向我介绍了这个想法.
https://www.regular-expressions.info/backref2.html - 本页的第一部分给出了简要说明.
在ECMAScript/Javascript正则表达式中,对NPCG的反向引用始终匹配(进行零长度匹配).在几乎所有其他正则表达式的味道,他们无法匹配任何东西.
如果我有以下C++代码来比较两个128位无符号整数,使用内联amd-64 asm:
struct uint128_t {
uint64_t lo, hi;
};
inline bool operator< (const uint128_t &a, const uint128_t &b)
{
uint64_t temp;
bool result;
__asm__(
"cmpq %3, %2;"
"sbbq %4, %1;"
"setc %0;"
: // outputs:
/*0*/"=r,1,2"(result),
/*1*/"=r,r,r"(temp)
: // inputs:
/*2*/"r,r,r"(a.lo),
/*3*/"emr,emr,emr"(b.lo),
/*4*/"emr,emr,emr"(b.hi),
"1"(a.hi));
return result;
}
Run Code Online (Sandbox Code Playgroud)
然后它将非常有效地内联,但有一个缺陷.返回值通过通用寄存器的"接口"完成,值为0或1.这会增加两个或三个不必要的额外指令,并减少比较操作,否则将完全优化.生成的代码看起来像这样:
mov r10, [r14]
mov r11, [r14+8]
cmp r10, [r15]
sbb r11, [r15+8]
setc al
movzx eax, al
test eax, eax
jnz is_lessthan
Run Code Online (Sandbox Code Playgroud)
如果我使用带有"bo"返回值的"sbb%0,%0"而不是带有"bool"返回值的"setc%0",那么还有两个额外的指令:
mov r10, [r14]
mov r11, [r14+8]
cmp r10, [r15] …Run Code Online (Sandbox Code Playgroud)