全局正则表达式匹配while()的反引号结果

时间:2017-04-06 13:49:45

标签: regex perl

此脚本搜索包含单词的行并打印它们,而在每次迭代中重新读取源文件

# cat mm.pl
#!/usr/bin/perl
use strict;
use warnings;

while( `cat aa` =~ /(\w+)/g ) {
    print "$1\n";
}

输入文件:

# cat aa
aa
bb
cc

结果:

# ./mm.pl
aa
bb
cc

请解释为什么运行脚本不是无穷无尽

在每个迭代时,regex引擎的重复偏移应该重置,因为表达式已更改(新的 cat 已分叉)。

我认为perl会对 cat 结果进行某种缓存,但是strace声称​​ cat 已经产生了4次(3行为3行,而条件为1则为假) :

# strace -f ./mm.pl 2>&1 | grep cat | grep -v ENOENT
[pid 22604] execve("/bin/cat", ["cat", "aa"], [/* 24 vars */] <unfinished ...>
[pid 22605] execve("/bin/cat", ["cat", "aa"], [/* 24 vars */] <unfinished ...>
[pid 22606] execve("/bin/cat", ["cat", "aa"], [/* 24 vars */] <unfinished ...>
[pid 22607] execve("/bin/cat", ["cat", "aa"], [/* 24 vars */] <unfinished ...>

另一方面,以下示例永远运行

# cat kk.pl
#!/usr/bin/perl
use strict;
use warnings;

my $d = 'aaa';
while( $d =~ /(\w+)/g ) {
    print "$1\n";
    $d = 'aaa';
}

两个脚本的区别在哪里?我错过了什么?

1 个答案:

答案 0 :(得分:7)

//g停止存储在魔法中的位置添加到执行匹配的标量中。

$ perl -MDevel::Peek -e'$_ = "abc"; Dump($_); /./g; Dump($_);'
SV = PV(0x32169a0) at 0x3253ee0
  REFCNT = 1
  FLAGS = (POK,IsCOW,pPOK)
  PV = 0x323bae0 "abc"\0
  CUR = 3
  LEN = 10
  COW_REFCNT = 1
SV = PVMG(0x326c040) at 0x3253ee0
  REFCNT = 1
  FLAGS = (SMG,POK,IsCOW,pPOK)
  IV = 0
  NV = 0
  PV = 0x323bae0 "abc"\0
  CUR = 3
  LEN = 10
  COW_REFCNT = 2
  MAGIC = 0x323d050
    MG_VIRTUAL = &PL_vtbl_mglob
    MG_TYPE = PERL_MAGIC_regex_global(g)
    MG_FLAGS = 0x40
      BYTES
    MG_LEN = 1

这意味着在反引号示例中观察到的行为的唯一方法是匹配运算符是否与评估它的所有四次匹配相同的标量!怎么可能?这是因为反引号是使用TARG的运算符之一。

创建标量相对昂贵,因为它需要最多三次内存分配!为了提高性能,称为TARG的标量与某些运算符的每个实例相关联。当评估具有TARG的运算符时,它可以使用要返回的值来填充TARG并返回TARG(而不是分配和返回新的TARG)。

“那又怎么样?”,你可能会问。毕竟,您已经证明分配给标量会重置与该标量相关联的匹配位置。这就是假设发生的事情,但它不适用于反叛。

Magic不仅允许将信息附加到变量,还附加在某些条件下调用的函数。由//g添加的魔法附加了一个函数,该函数应在标量修改后调用(由上面转储中的SMG标志指示)。当一个值被赋值给标量时,此函数将清除该位置。

赋值运算符正确处理魔法,但不是由反引号运算符处理。它不会期望魔法被添加到它的TARG中,所以它不会检查是否有任何,所以清除匹配位置的函数不会被调用。