正则表达式 – Perl正则表达式引擎错误?

我一直在尝试编写一个正则表达式来验证文件,以确保它遵循特定的格式.该文件应该有一个版本();行后面跟一个或多个元素();块.

以下是有效文件的示例:

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似乎仍然认为这个文本是有效的!当我运行此代码时,它会生成输出

match

我花了好几个小时试图找出我的正则表达式出了什么问题,但我只是没有看到它.我甚至使用online regular expression tester测试了正确的正则表达式,根据测试我的正则表达式应该工作正常! (如果要在格式有效时看到它确实匹配,请尝试删除垃圾”行.)

这让我整天彻底难过,让我想知道Perl正则表达式引擎本身是否存在错误.有人可以告诉我为什么这不匹配的时候不应该?

我使用的是perl v5.20.1

解决方法

非贪婪的比赛一旦满足就不会停止.它试图尽快继续.如果正则表达式的其余部分无法匹配,则仍会发生回溯 – 但对于非贪婪的量词,回溯意味着匹配更多.

避免这种情况的一种可能性在于回溯控制.例如,您可能希望在版本最初匹配后禁止回溯.我们可以通过(?> …)构造来做到这一点.这与外部模式独​​立地匹配包含的模式.如果模式的其余部分失败,则回溯将不会继续进入包含的模式,但会跳过整个包含的模式.描述这个有点困难,详情请见perldoc perlre.

添加到量词(例如,?,*)具有与(?> …)类似的效果.在有效的正则表达式中,优选这些无回溯量词和(?> …)组是非常可取的.

具体来说,替换

(?<valid_text>
    \A\s*(?&version)\s*
    (?: (?&element) \s* )+
    \s*\Z
)

(?<valid_text>
    \A\s*(?>(?&version))\s*
    (?: (?&element) \s* )++
    \s*\Z
)

作为另一种选择,您可以使用(* PRUNE)回溯控制动词.遇到PRUNE命令后,不会发生超过该点的回溯.这使得匹配到目前为止所选择的替代方案.

(?<valid_text>
    \A\s*(?&version)\s* (*PRUNE)
    (?: (?&element) \s* )+
    \s*\Z
)

相关文章

正则替换html代码中img标签的src值在开发富文本信息在移动端...
正则表达式
AWK是一种处理文本文件的语言,是一个强大的文件分析工具。它...
正则表达式是特殊的字符序列,利用事先定义好的特定字符以及...
Python界一名小学生,热心分享编程学习。
收集整理每周优质开发者内容,包括、、等方面。每周五定期发...