3

最近、Perlスクリプトの1つで奇妙な結果に遭遇しました。そこでは、NULL文字(Perlでは\ 0)がテキストに導入されていました。最終的に、Perl m//match演算子で誤って使用されている//g演算子まで追跡しました。これが起こるまで、私はs ///演算子でしか使用していなかったので、m//演算子で//gを使用できることに気づいていませんでした。

いずれにせよ、誤った// 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の第一人者はこの振る舞いを説明できますか?グーグルで何も見つかりませんでした。

4

4 に答える 4

5
if ($text =~ m/(\d+)/g)

間違っている。具体的には、フォームのコードif (/.../g)が間違っています。概念的には意味がなく (「一致する場合は一致しないまで」???)、望ましくない結果が生じる可能性があります。

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

「グ」を取り除きます。


通常、文字列の末尾には 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 は修正を含む最初の製品リリースです。

于 2011-12-29T21:01:11.773 に答える
4

これは明らかにバグです。最新バージョンで確認してください。それでも問題が解決しない場合は、バグ レポートを提出する方法を次に示します。

http://perldoc.perl.org/perlbug.html

于 2011-12-29T20:21:30.267 に答える
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まだ文字列の最初の 2 文字です。

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

$testこの問題は、同じステートメントでandを使用する代わりに新しい変数に代入する場合には発生しません$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
于 2011-12-30T06:25:23.853 に答える
0
$ perl -e '$text = "01"; if ($text =~ m/(\d+)/g) { $text = "A$1"; }; print "$text\n"; print "Contains nul" if $text =~ m/\0/''
A01

(パール 5.12.4)

@ダンが言うように、これはバグです。

于 2011-12-29T20:25:32.157 に答える