Regex 为什么Perl';s m//g运算符有时会导致在文本中引入空值?

Regex 为什么Perl';s m//g运算符有时会导致在文本中引入空值?,regex,perl,side-effects,Regex,Perl,Side Effects,我们最近在一个Perl脚本中遇到了一些奇怪的结果,其中在一些文本中引入了空字符(\0)。我们最终找到了Perl m//match操作符上意外使用的//g操作符。在此之前,我甚至不知道可以将//g与m//运算符一起使用,因为我只将它与s///运算符一起使用 无论如何,即使我们已经通过删除错误的//g修复了这个bug,我还是想知道为什么这个小脚本会在文本中引入空字符!:-) 防止出现NULL的细微更改:如果我更改$text的值(例如,仅更改为“0”或“1”或许多其他组合),则不再引入NULL。如果我

我们最近在一个Perl脚本中遇到了一些奇怪的结果,其中在一些文本中引入了空字符(\0)。我们最终找到了Perl m//match操作符上意外使用的//g操作符。在此之前,我甚至不知道可以将//g与m//运算符一起使用,因为我只将它与s///运算符一起使用

无论如何,即使我们已经通过删除错误的//g修复了这个bug,我还是想知道为什么这个小脚本会在文本中引入空字符!:-)

防止出现NULL的细微更改:如果我更改$text的值(例如,仅更改为“0”或“1”或许多其他组合),则不再引入NULL。如果我将赋值从“A$1”更改为“$1”,则不再引入NULL。如果我将“A$1”分配给一个完全不同的变量,则该变量中不会引入NULL。如果在m//匹配过程中删除//g运算符,则不会引入NULL


Perl专家能解释一下这种行为吗?我在谷歌上找不到任何东西。

这显然是一个bug。检查最新版本,如果仍然存在问题,下面介绍如何提交错误报告:

(perl 5.12.4)

正如@Dan所说,这是一个bug

if ($text =~ m/(\d+)/g)
这是错误的。具体来说,
形式的代码if(/…/g)
是错误的。它在概念上毫无意义(“如果匹配,直到它不匹配”?),并且可能会产生不希望的结果

$_ = "01ab";
if (/(\d+)/g) { say $1; }   # 01
if (/(.*)/g)  { say $1; }   # ab!!!
去掉“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
您的Perl版本似乎有一个bug,当匹配的起始位置在字符串的末尾时,它匹配该NUL。没有插入NUL。幸运的是,如果您修复了错误代码,您就不会受到此错误的影响


显示它已由修复


基于
git标记——包含6f1401dc2acd2a2b85df22b0a74e5f7e6e0a33aa
,5.13.2是第一个开发版本,5.14.0是第一个有修复的生产版本。

有一个
perl
错误,但也有编程问题。不要依赖特殊变量的值,除非在设置后立即语句中。立即存储它们的值

当您遇到这些问题时,请查看数据。这是一个奇怪的问题,看起来像是处理捕获缓冲区的bug

use v5.10;
use feature qw(unicode_strings);

my $text = "01";

if ($text =~ m/(\d+)/g)
{
    say "\$1 [$1]: ", join ' ', map { sprintf '%04X', ord } split //, $1;
    say 'Text: ', join ' ', map { sprintf '%04X', ord } split //, $text;

    $text = "A$1";
    say "\$1 [$1]: ", join ' ', map { sprintf '%04X', ord } split //, $1;
    say 'Text: ', join ' ', map { sprintf '%04X', ord } split //, $text;
}
在您真正想要使用
$1
构建要分配给同一变量的新字符串之前,一切看起来都是正确的,此时该值似乎消失了。请注意,分配后,
$1
不同:

% perl5.12.2 test.pl
$1 [01]: 0030 0031
Text: 0030 0031
$1 [AA]: 0041 0041
Text: 0041 0041 0000
这在一个奇怪的方面也是不同的
perl
进行了一些复杂的处理来记住字符串中的偏移量。对于v5.14,
$1
仍然是字符串中的前两个字符:

% perl5.14.2 test.pl
$1 [01]: 0030 0031
Text: 0030 0031
$1 [A0]: 0041 0030
Text: 0041 0030 0031
如果您指定一个新变量,而不是在同一语句中使用
$test
$1
,则不会出现此问题(这应该很好,但我们都知道“应该”通常是什么意思)。如果您立即捕获特殊变量的值,这也不是问题:

use v5.10;
use feature qw(unicode_strings);

my $text = "01";

if ($text =~ m/(\d+)/g)
{
    my $one = $1;
    say "\$1 [$1]: ", join ' ', map { sprintf '%04X', ord } split //, $1;
    say 'Text: ', join ' ', map { sprintf '%04X', ord } split //, $text;

    $text = "A$one";
    say "\$1 [$1]: ", join ' ', map { sprintf '%04X', ord } split //, $1;
    say 'Text: ', join ' ', map { sprintf '%04X', ord } split //, $text;
}
现在,即使是v5.12也能做到:

$ perl5.12.2 test.pl
$1 [01]: 0030 0031
Text: 0030 0031
$1 [A0]: 0041 0030
Text: 0041 0030 0031

我在Perl 5.10.1和5.12.2中看到了这个问题。我在5.13.6或5.14.0中没有看到它。看起来问题已经解决了。我在Perl 5.10.1和5.12.2中看到了问题。我在5.13.6或5.14.0中没有看到它。看起来它已经被修复了。我一直使用
if(/…/g)
;在标量上下文中,/g“直到”@ysth,
,而(//g)
是有意义的
if(//gc)
是一种有意义的高级用法<代码>如果(//g)没有那么多。如果您使用
if(//g)
,我假设您正在展开while循环,在这种情况下,您知道您打算这么做。但是你知道这一点,那么你的观点是什么?是的,我的意思是使用/c,但是在/gc ifs链的末尾不使用/c是有意义的。但我的观点是“匹配直到不匹配”不是一个准确的描述。@ysth,这是我的观点。无论他们认为这意味着什么,都不可能是准确的。感谢对此的反馈,使用Devel::Peek是我没有想到要测试的。我同意,在if()语句中使用//g是没有意义的,但是在阅读了//g可以与m//一起使用之后,我只是好奇NUL如何插入到所示的代码中。但正如@Dan指出的和Keith Thompson确认的那样,似乎NUL“插入”行为最终是一个bug,它已在较新版本的Perl中修复。
% perl5.14.2 test.pl
$1 [01]: 0030 0031
Text: 0030 0031
$1 [A0]: 0041 0030
Text: 0041 0030 0031
use v5.10;
use feature qw(unicode_strings);

my $text = "01";

if ($text =~ m/(\d+)/g)
{
    my $one = $1;
    say "\$1 [$1]: ", join ' ', map { sprintf '%04X', ord } split //, $1;
    say 'Text: ', join ' ', map { sprintf '%04X', ord } split //, $text;

    $text = "A$one";
    say "\$1 [$1]: ", join ' ', map { sprintf '%04X', ord } split //, $1;
    say 'Text: ', join ' ', map { sprintf '%04X', ord } split //, $text;
}
$ perl5.12.2 test.pl
$1 [01]: 0030 0031
Text: 0030 0031
$1 [A0]: 0041 0030
Text: 0041 0030 0031