为什么Perl会抱怨\ Q .. \ E正则表达式部分中无法匹配的括号?

时间:2014-01-25 01:37:19

标签: regex perl

我在变量中有一个正则表达式,包含一个包含开括号的\Q...\E内的子字符串。我希望解析器将[解释为一个vanilla字符,因为它位于\Q...\E部分内。

当正则表达式作为程序中的文字出现时似乎就是这种情况,但是当它出现在变量中时,解析器就会失败。

这是一个简化的例子。

这有效:

$r = qr/\Qa[b\E\d+/;
if ("a[b1" =~ $r) { print "match\n"; }

这失败了:

$v='\Qa[b\E\d+';
$r=qr/$v/;

它在第2行死亡

  

无与伦比[正则表达式;标记为< - HERE in m / \ Qa [< - HERE b \ E \ d + /

为什么Perl会拒绝这个?并且只有在从变量进行插值而不是使用相同的正则表达式内联时才会出现?

我无法在常见问题解答How do I match a regular expression that's in a variable?或perlop Regexp Quote-Like Operators中看到任何解释。

如果版本很重要,则使用Perl 5.14.2(Ubuntu 12.04),默认设置。

3 个答案:

答案 0 :(得分:6)

\Q与正则表达式无关。当正则表达式引擎看到\Q时,它无法识别它,会发出警告,并将其视为\\Q

>perl -we"$re='\Qa'; qr/$re/
Unrecognized escape \Q passed through in regex; marked by <-- HERE in m/\Q <-- HERE a/ at -e line 1.

与插值类似,\Q由双引号字符串文字识别,类似。像插值一样,它必须是文字(Perl代码)的一部分才能工作。

>perl -E"$pat=q{\Q!}; say qr/$pat/"
(?^u:\Q!)

>perl -E"$pat=qq{\Q!}; say qr/$pat/"
(?^u:\!)

>perl -E"$x='!'; $pat=q{$x}; say qr/$pat/"
(?^u:$x)

>perl -E"$x='!'; $pat=qq{$x}; say qr/$pat/"
(?^u:!)

解决方案:

  • $v="\Qa[b\E\\d+";
  • $v=qr/\Qa[b\E\d+/;
  • $v=quotemeta('a[b').'\d+';

答案 1 :(得分:3)

首先评估Perl正则表达式,就好像它是一个简单的双引号字符串。内插任何嵌入变量,并处理源自插值变量的转义序列。这就是\L\U\Q...\E等特殊运营商的行动点。

处理在双引号字符串中停止,但在正则表达式中,字符串然后编译

在您的示例中,您有

$v = '\Qa[b\E\d+';

并且因为您使用了单引号,所以此字符串根本不会更改。

然后使用

将其插入到正则表达式中
$r = qr/$v/;

但是,因为内插变量中的转义序列不受影响,所以字符串将原样传递给正则表达式编译器,该编译器报告该表达式无效,因为它包含一个不匹配的非转义开放括号。如果删除该括号,仍会出现错误;这次Unrecognized escape \Q passed through in regex显示\Q...\E尚未处理并显示为文字。

的工作原理是将您的作业更改为$v以改为使用双引号,例如

my $v = "\Qa[b\E\\d+";

\d上的反斜杠必须加倍,否则就会消失。现在\Q...\E已被采取行动,$v等于a\[b\d+。将其编译为正则表达式可以正常工作。

答案 2 :(得分:2)

在解析正则表达式时解释\ Q和\ E元字符。它们不是正则表达式本身的一部分。如果\ Q和\ E出现在正则表达式文字中,它们会告诉解析器忽略通常在正则表达式中具有特殊含义的字符,包括括号。如果\ Q和\ E作为变量赋值的一部分出现在单引号中,则将它们视为文字字符串。当在正则表达式中使用此变量时,文字值将成为正则表达式的一部分。反斜杠被解释为转义符,因此\ Q匹配文字Q,\ E匹配文字E.

要看到这一点,请尝试编译正则表达式然后打印它:

$v=qr/\Qa[b\E\d+/;
print "$v\n";

输出结果为:

(?-xism:a\[b\d+)

请注意\ Q和\ E已消失,并且括号已被转义。如果在单引号内单独指定包含\ Q和\ E的字符串:

$v='ab\Qcd\Eef';
$r=qr/$v/;
print "$r\n";

你得到:

(?-xism:ab\Qcd\Eef)

这个正则表达式实际匹配“abQcdEef”:

$v='ab\Qcd\Eef';
$r=qr/$v/;
if("abQcdEef" =~ /$r/) {print "matches\n"} else {print "no match\n"}

结果:

matches