我们最近在一个Perl脚本中遇到了一些奇怪的结果,其中NULL字符(Perl中的\ 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";
}
阻止NULL出现的细微更改:如果我更改$ text的值(例如,更改为“0”或仅“1”或许多其他组合),则不再引入NULL。如果我将赋值值从“A $ 1”更改为“$ 1”,则不再引入NULL。如果我将“A $ 1”分配给完全不同的变量,则不会将NULL引入该变量。如果我在m //匹配期间删除了// g运算符,则不会引入NULL。
Perl大师可以解释一下这种行为吗?我用谷歌搜索找不到任何东西。
答案 0 :(得分:5)
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版本似乎有一个与NUL匹配的错误。没有插入NUL。幸运的是,如果你修复了你的错误代码,你将不会遇到这个错误。
../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);
'
显示它由6f1401dc2acd2a2b85df22b0a74e5f7e6e0a33aa修复。
基于git tag --contains 6f1401dc2acd2a2b85df22b0a74e5f7e6e0a33aa
,5.13.2是第一个开发版本,5.14.0是第一个获得修复的生产版本。
答案 1 :(得分:4)
这显然是一个错误。检查最新版本,如果它仍然是一个问题,这里是如何提交错误报告:
答案 2 :(得分:2)
有一个perl
错误,但您也遇到了编程问题。除了设置后的立即语句外,不要依赖特殊变量的值。立即存储它们的值。
遇到这些问题时,请查看数据。事实证明这是一个奇怪的问题,看起来像处理捕获缓冲区的错误。
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
答案 3 :(得分:0)
$ perl -e '$text = "01"; if ($text =~ m/(\d+)/g) { $text = "A$1"; }; print "$text\n"; print "Contains nul" if $text =~ m/\0/''
A01
(perl 5.12.4)
正如@Dan所说,这是一个错误。