在perl中,为什么在不使用双引号引起来的情况下push会导致qr创建的正则表达式被更改?

问题描述

我正在使用qr在脚本中定义正则表达式,并将其推入数组。但是现在看来,如果我不将正则表达式放在双引号中,则将其压入数组的操作会对其进行更改。示例:

#!/usr/bin/perl
use strict; use warnings;
use Data::Dumper qw(Dumper);
use Data::Dumper::Concise;
my @regexes;
my $rgx = 'dog'; my $mdf = 'i';
$rgx = join ( '','(?',$mdf,')',$rgx ) if ($mdf); # in production,$mdf Could be empty
eval { $rgx = qr/$rgx/ };
if ($@) # catch illegitimate regex modifier,such as 'g'
{
   die "rgx==$rgx; mdf==$mdf; qr throws an error";
}
push @regexes,$rgx;
push @regexes,"$rgx";
print "first try just printing \$rgx\n";
print " no double quotes:";
print $rgx; print "\n";
print "yes double quotes:";
print "$rgx"; print "\n";
print "but Now see what happens when I push it onto an array\n";
print Dumper \@regexes;

这将产生什么:

first try just printing $rgx
 no double quotes:(?^:(?i)dog)
yes double quotes:(?^:(?i)dog)
but Now see what happens when I push it onto an array
[
  qr/(?i)dog/i,"(?^:(?i)dog)"
]

我认为(?^:(?i)dog)是成品,可以进行正则表达式匹配,例如

if ( /$rgx/ )

,实际上,这就是为什么我通过qr运行预期的正则表达式。

push为什么要更改它?

为什么会产生特定的语法, qr/(?i)dog/i

解决方法

您实际上是在询问传回的值之间的差异

my $rgx = qr/$rgx/; $rgx

my $rgx = qr/$rgx/; "$rgx"

qr//编译提供的正则表达式模式,并返回一个代表已编译表单的对象。这是存储在变量$rgx中的值,由表达式$rgx返回。

""生成一个字符串,因此"$rgx"提供了$rgx的字符串化。值得庆幸的是,这将返回一个字符串,该字符串可用作已编译对象表示的正则表达式模式。但是,通过执行"$rgx",您实际上可以撤消qr/$rgx/所做的工作。

Data :: Dumper使用qr//文字表示正则表达式对象,并使用""文字表示字符串。

,

不同表示形式的模式相同。 Data::Dumper对对象进行特殊的字符串表示,而在对它进行插值时,正则表达式对象本身也会创建不同的表示。

也许我来自有效Perl 的文章可以帮助您:Let perl create your regex stringification

,

由于我在回答自己的问题,所以我可能对海报(我自己)不敬。我问了两个问题。

第一个答案是:“取消提问。push不会更改正则表达式。”

第二个答案是:同样,push不会产生该特定语法。 Data::Dumper包产生了令人费解的语法。 以下代码对此进行了演示。

#!/usr/bin/perl
use strict; use warnings;
use Data::Dumper qw(Dumper); use Data::Dumper::Concise;
my $rgx = 'dog';
my $mdf = 'is';
$rgx = join ( '','(?',$mdf,')',$rgx ) if ($mdf); # in production,$mdf could be empty
print "   no quotes rgx=="; print $rgx; print ";\n"; print "      quotes rgx=="; print "$rgx"; print ";\n";
print "DD no quotes rgx=="; print Dumper $rgx; print "DD    quotes rgx=="; print Dumper "$rgx"; 
$rgx = qr/$rgx/;
print "\nNow,after qr:\n";
print "   no quotes rgx=="; print $rgx; print ";\n"; print "      quotes rgx=="; print "$rgx"; print ";\n";
print "DD no quotes rgx=="; print Dumper $rgx; print "DD    quotes rgx=="; print Dumper "$rgx"; 

及其打印内容:

   no quotes rgx==(?is)dog;
      quotes rgx==(?is)dog;
DD no quotes rgx=="(?is)dog"
DD    quotes rgx=="(?is)dog"

Now,after qr:
   no quotes rgx==(?^:(?is)dog);
      quotes rgx==(?^:(?is)dog);
DD no quotes rgx==qr/(?is)dog/si
DD    quotes rgx=="(?^:(?is)dog)"

已经说过qr“编译”一个正则表达式。由于我以前作为学生使用汇编语言(FORTRAN,Pascal)编写代码的经历,我认为我对该术语有误解。从https://perldoc.perl.org/perldata#Scalar-values

标量不一定是一回事。没有地方将标量变量声明为“字符串”类型,“数字”类型,“引用”类型或其他任何类型。由于标量的自动转换,返回标量的操作不需要关心(实际上也不需要关心)其调用者是在寻找字符串,数字还是引用。 Perl是上下文多态的语言,其标量可以是字符串,数字或引用(包括对象)。

如果我没看错的话,qr的输出将不是“二进制”或类似于Pascal目标代码的输出。 print正是在示例代码中显示的内容。

因此,我以为push是一个有罪的聚会而偏离了基础。看来Dumper通过将某些标量恰好可解释为正则表达式,可以自动将其转换为正斜杠,方法是将它们放在正斜杠中。而且Dumper显然是从正则表达式的括号内复制正则表达式标志,对其进行重新排序(请注意,它会将double标志从is更改为si),并将其放在第二个正后斜线。