为什么Perl的m // g运算符有时会导致将NULL引入文本?

Mas*_*iti 3 regex perl side-effects

我们最近在一个Perl脚本中遇到了一些奇怪的结果,其中NULL字符(Pe​​rl中的\ 0)被引入到某些文本中.我们最终将其跟踪到偶然用于Perl m //匹配运算符的// g运算符.直到发生这种情况,我甚至都不知道你可以使用// g和m //运算符,因为我只使用它与s ///运算符.

无论如何,即使我们通过删除错误// g来修复错误,我也很想知道为什么这个小脚本在文本中引入了一个NULL字符!:-)

my $text = "01";

if ($text =~ m/(\d+)/g)
{
    $text = "A$1";
}

if ($text =~ m/\0/)
{
    print "Text contains NULL!\n";
}
Run Code Online (Sandbox Code Playgroud)

阻止NULL出现的细微更改:如果我更改$ text的值(例如,更改为"0"或只是"1"或许多其他组合),则不再引入NULL.如果我将赋值值从"A $ 1"更改为"$ 1",则不再引入NULL.如果我将"A $ 1"分配给完全不同的变量,则不会将NULL引入该变量.如果我在m //匹配期间删除了// g运算符,则不会引入NULL.

Perl大师可以解释一下这种行为吗?我用谷歌搜索找不到任何东西.

ike*_*ami 5

if ($text =~ m/(\d+)/g)
Run Code Online (Sandbox Code Playgroud)

是错的.具体来说,表单的代码if (/.../g)是错误的.它在概念上没有任何意义("如果匹配,直到它不匹配"???)并且可以给出不希望的结果.

$_ = "01ab";
if (/(\d+)/g) { say $1; }   # 01
if (/(.*)/g)  { say $1; }   # ab!!!
Run Code Online (Sandbox Code Playgroud)

摆脱"g".


字符串的结尾通常后跟NUL.

$ perl -MDevel::Peek -e'Dump "01"'
SV = PV(0x88b4740) at 0x88d1368
  REFCNT = 1
  FLAGS = (PADTMP,POK,READONLY,pPOK)
  PV = 0x88d52f0 "01"\0
  CUR = 2
  LEN = 12
Run Code Online (Sandbox Code Playgroud)

您的Perl版本似乎有一个错误,当匹配的起始位置在字符串的末尾时,它与NUL匹配.没有插入NUL.幸运的是,如果你修复了你的错误代码,你就不会受到这个bug的影响.


../perl/Porting/bisect.pl           \
   --target=miniperl --expect-fail  \
   --start=v5.13.0 --end=v5.14.0    \
   -e'
      my $text = "01";
      if ($text =~ m/(\d+)/g) { $text = "A$1"; }
      exit($text =~ m/\0/ ? 1 : 0);
   '
Run Code Online (Sandbox Code Playgroud)

显示它由6f1401dc2acd2a2b85df22b0a74e5f7e6e0a33aa修复.

基于git tag --contains 6f1401dc2acd2a2b85df22b0a74e5f7e6e0a33aa5.13.2是第一个开发版本,5.14.0是第一个有修复版本的生产版本.

  • 我一直使用`if(/...//)` 在标量上下文中,/ g不是"直到" (4认同)
  • @ysth,“while (//g)”是有道理的。`if (//gc)` 是一个有意义的高级用法。`if (//g)` 没那么多。我认为如果您使用 `if (//g)`,您就会展开 while 循环,在这种情况下您知道您打算这样做。但你知道这一点,那么你的观点是什么? (2认同)