Regex Perl正则表达式引擎错误?
我一直在尝试编写一个正则表达式来验证文件,以确保它遵循特定的格式。该文件应具有Regex Perl正则表达式引擎错误?,regex,perl,validation,debugging,pcre,Regex,Perl,Validation,Debugging,Pcre,我一直在尝试编写一个正则表达式来验证文件,以确保它遵循特定的格式。该文件应具有版本()行,然后后跟一个或多个元素()块 以下是一个有效文件的示例: version(1.0); element ( ); element ( ); element ( ); 作为测试,我创建了以下Perl示例: use strict; use warnings; my $text = <<'END_TEXT'; version(1.0); element ( ); garbage <-
版本()代码>行,然后后跟一个或多个元素()代码>块
以下是一个有效文件的示例:
version(1.0);
element
(
);
element
(
);
element
(
);
作为测试,我创建了以下Perl示例:
use strict;
use warnings;
my $text = <<'END_TEXT';
version(1.0);
element
(
);
garbage <--- THIS SHOULD NOT MATCH!
element
(
);
element
(
);
END_TEXT
my $rx_defs = qr{(?(DEFINE)
(?<valid_text>
\A\s*(?&version)\s*
(?: (?&element) \s* )+
\s*\Z
)
(?<version>
version\(.+?\);
)
(?<element>
element\s*
(?&element_body);
)
(?<element_body>
\( (?: [^()]++ | (?&element_body) )* \)
)
)}xms;
if ($text =~ m/(?&valid_text)$rx_defs/) {
print "match";
}
我花了几个小时试图找出正则表达式的错误,但我就是看不到它。我甚至使用了一个测试来测试正则表达式,根据测试,我的正则表达式应该可以正常工作!(如果希望在格式有效时查看“垃圾”行是否正确匹配,请尝试删除该行。)
这让我一整天都感到困惑,我想知道Perl正则表达式引擎本身是否存在bug。有人能告诉我为什么这是匹配的,而它不应该
我使用的是perl v5.20.1非贪婪匹配不会在满足时立即停止。它试图尽快继续。如果正则表达式的其余部分无法匹配,那么回溯仍然会发生——但对于非贪婪量词,回溯意味着匹配更多
避免这种情况的一种可能性在于回溯控制。例如,您可能希望在版本
初始匹配后禁止回溯。我们可以通过(?>…)
构造来实现这一点。这与包含的模式独立于外部模式进行匹配。如果模式的其余部分失败,回溯将不会继续到包含的模式中,而是跳过整个包含的模式。描述这一点有点困难,请参阅了解详细信息
将++
添加到量词(如++
,?++
,*++
)具有与(?>…)
类似的效果。在高效正则表达式中,最好使用这些无回溯量词和(?>…)
组
具体来说,替换
(?<valid_text>
\A\s*(?&version)\s*
(?: (?&element) \s* )+
\s*\Z
)
来自PCRE文档,网址为:
直到PCRE2 10.23版,子例程调用(无论是否递归)一直被视为原子组,但从10.30版开始,这种情况发生了变化,现在支持回溯到子例程调用,就像在Perl中一样
regex101使用PHP运行PCRE。据介绍,PHP只支持PCRE1(8.x分支)。因此,regex101不支持回溯到子例程调用中
。。。这就是这里发生的事情:
- 我们进入
(?&valid\u text>)
并尝试匹配\A\s*(?&version)\s*
\A
(字符串开头)和\s*
(可选空格)很简单
(?&version)
不执行version\(.+?\)代码>
- 这与输入的以下部分匹配:
version();
element
(
);
版本(
按字面匹配。下一个字符)
由+?
(至少需要一个字符匹配)使用。然后+?
慢慢地消耗越来越多的字符(它不是贪婪的),直到到达)代码>。第一次发生这种情况是在消费之后;元素(
),所以我们现在就到此为止
(?&version)
调用返回
- 我们使用以下任何空格
- 下一部分是
(?:(?&element)\s*)+
,即一个或多个元素,每个元素后跟可选空格
(?&element)
不执行element\s*
,即它必须以element
- 我们当前在输入中的位置是
垃圾…
,因此此操作失败
此时正则表达式引擎尝试回溯。在PCRE<10.30中,唯一可以回溯的部分是\s*
(即“可选空白”位),但匹配较少的空白字符也不会导致成功匹配,因此整个过程很快失败
但是,在Perl中,我们可以回溯到子例程调用:我们重新输入(?&version)
,让+?
匹配更多字符(直到找到下一个);
),然后重试(?&element)
。这最终会让(?&version)
消耗垃圾
和下面的元素
,从而允许整个正则表达式成功
有人能告诉我为什么这是匹配的,而它不应该
我不明白你为什么认为它不匹配。:-)
它在PHP中不匹配的唯一原因是它使用的旧PCRE版本有限制。这一点看起来可疑:version\(.+?\)代码>–paren可以包含元素和垃圾。考虑限制内容,例如“<代码> [^ ] ] + < /代码>。但是怎么办???一个非贪婪的匹配应该在满足后立即停止!还有,为什么这在perl中匹配,而在使用“PCRE”作为regex类型的regex测试网站上不匹配?它们的功能应该相同,不是吗?一个非常酷的正则表达式,毫无疑问,正则表达式是一个伟大的工具,毫无疑问。。。但我忍不住要说:这很难,但可以通过使用处理嵌套/平衡分隔符的工具来避免。提示:这绝不是引擎/语言/编译器的bug;这总是一个用户错误。好。。。除非。。。但这是一长串可能的行为原因中的最后一件事。(我会重新考虑这样的标题;你真的确定吗?)我试图解析的真实文本在version()
的括号之间有文本。我的例子似乎有点过于简单化了,但即使有其他的+?
文本,它仍然匹配。我想这是正确的行为,尽管它非常令人困惑。@tjwrona1992任何时候你在正则表达式中有*
或+
,这都是一个潜在的错误*
可以并且将跳过任何文本,如果这是使整个正则表达式成功的必要条件(除非我
(?<valid_text>
\A\s*(?>(?&version))\s*
(?: (?&element) \s* )++
\s*\Z
)
(?<valid_text>
\A\s*(?&version)\s* (*PRUNE)
(?: (?&element) \s* )+
\s*\Z
)
version();
element
(
);