Почему оператор Perl m // g иногда приводит к тому, что в текст вводятся значения NULL? - PullRequest
3 голосов
/ 30 декабря 2011

Недавно мы столкнулись с некоторыми странными результатами в одном из наших сценариев Perl, где в некоторый текст вводился символ NULL (\ 0 в Perl). В конечном итоге мы отследили его до оператора // g, который случайно использовался в операторе Perl m // match. Пока этого не произошло, я даже не знал, что вы можете использовать // 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. И если я удаляю оператор // g во время совпадения m //, значение NULL не вводится.

Может ли Perl-гуру объяснить это поведение, пожалуйста? Я ничего не мог найти, прибегая к помощи.

Ответы [ 4 ]

5 голосов
/ 30 декабря 2011
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 является первым рабочим выпуском, в котором есть исправление.

4 голосов
/ 30 декабря 2011

Это явно ошибка. Проверьте это на последней версии, если это все еще проблема, вот как подать отчет об ошибке:

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

2 голосов
/ 30 декабря 2011

Есть ошибка 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
0 голосов
/ 30 декабря 2011
$ 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, это ошибка.

...