Сохранение снимков с оператором замены Perl - PullRequest
6 голосов
/ 29 марта 2011

Может кто-нибудь объяснить, почему следующий код ...

#!/opt/local/bin/perl
use strict;
use warnings;

my $string;

$string = "\t\t\tEntry";
print "String: >$string<\n";

$string =~ s/^(\t*)//gi;

print "\$1: >$1<\n";
print "String: >$string<\n";
print "\n";

$string = "\t\t\tEntry";

$string =~ s/^(\t*)([^\t]+)/$2/gi;

print "\$1: >$1<\n";
print "String: >$string<\n";
print "\n";

exit 0;

... выдает следующий вывод ...

String: >           Entry<
Use of uninitialized value in concatenation (.) or string at ~/sandbox.pl line 12.
$1: ><
String: >Entry<

$1: >           <
String: >Entry<

... или, более точно: почему совпадающее значение в первой замене не сохраняется в $ 1?

Ответы [ 2 ]

7 голосов
/ 29 марта 2011

Я попробовал это на двух реализациях Perl 5.12, и не столкнулся с проблемой.5,8 сделал.

Поскольку у вас есть опции g, perl пытается сопоставить шаблон, пока он не завершится неудачей.Смотрите вывод отладки ниже.

Так что он не работает в Perl 5.8, но this делает:

my $c1;
$string =~ s/^(\t*)/$c1=$1;''/ge;

Таким образом, каждый раз, когда он совпадает, он сохраняет егона $c1.

Это то, что use re 'debug' говорит мне:

Compiling REx `^(\t*)'
size 9 Got 76 bytes for offset annotations.
first at 2
   1: BOL(2)
   2: OPEN1(4)
   4:   STAR(7)
   5:     EXACT <\t>(0)
   7: CLOSE1(9)
   9: END(0)
anchored(BOL) minlen 0
Offsets: [9]
        1[1] 2[1] 0[0] 5[1] 3[1] 0[0] 6[1] 0[0] 7[0]
Compiling REx `^(\t*)([^\t]+)'
size 25 Got 204 bytes for offset annotations.
first at 2
   1: BOL(2)
   2: OPEN1(4)
   4:   STAR(7)
   5:     EXACTF <\t>(0)
   7: CLOSE1(9)
   9: OPEN2(11)
  11:   PLUS(23)
  12:     ANYOF[\0-\10\12-\377{unicode_all}](0)
  23: CLOSE2(25)
  25: END(0)
anchored(BOL) minlen 1
Offsets: [25]
        1[1] 2[1] 0[0] 5[1] 3[1] 0[0] 6[1] 0[0] 7[1] 0[0] 13[1] 8[5] 0[0] 0[0] 0[0] 0[0] 0[0] 0[0] 0[0] 0[0] 0[0] 0[0] 14[1] 0[0] 15[0]
String: >                       Entry<
Matching REx `^(\t*)' against `                 Entry'
  Setting an EVAL scope, savestack=5
   0 <> <                       Entry>        |  1:  BOL
   0 <> <                       Entry>        |  2:  OPEN1
   0 <> <                       Entry>        |  4:  STAR
                           EXACT <\t> can match 3 times out of 2147483647...
  Setting an EVAL scope, savestack=5
   3 <                  > <Entry>        |  7:    CLOSE1
   3 <                  > <Entry>        |  9:    END
Match successful!
match pos=0
Use of uninitialized value in substitution iterator at - line 11.
Matching REx `^(\t*)' against `Entry'
  Setting an EVAL scope, savestack=5
   3 <                  > <Entry>        |  1:  BOL
                            failed...
Match failed
Freeing REx: `"^(\\t*)"'
Freeing REx: `"^(\\t*)([^\\t]+)"'

Поскольку вы пытаетесь сопоставить пробел в начале строки, вам не нужен ни gни i.Так что это может быть случай, когда вы пытаетесь сделать что-то еще.

2 голосов
/ 29 марта 2011

Я думаю, что версия 5.10 и выше, она влияет на буферы захвата, только если было совпадение.
Интересная вещь в вашем примере, что при $string =~ s/^(\t*)([^\t]+)/$2/gi;
она не сбрасывала буферы захвата.Похоже, что это из-за преамбулы, которая оценивает
, если соответствие должно быть проверено.В этом случае ([^\t]+) потребляет всю строку в первом совпадении
, поэтому произошло string too short, и буферы никогда не были сброшены.

Я не могу проверить это, но $string =~ s/^(\t*)([^\t])//gi должен выдать то же предупреждение.
if ( s///g ) {}, и тестирование буферов захвата в этом случае не обязательно будет содержать
что-либо.Это имело место в версии 5.8.Даже в более поздних версиях это действительно просто функция отладки.

Редактировать @theracoon - в вашем комментарии: "Я вполне уверен, что ([^ \ t] +) фактически не использовал всю строку. Вывод определенно делаетне отражайте это. "

Это доказательство того, что ваше регулярное выражение поглотило всю строку в первом совпадении, проход 1.
На втором проходе не осталось ничего, чтобы соответствовать.Именно так работает модификатор / g.
Он пытается снова сопоставить все регулярные выражения в позиции в строке, где остановилось последнее совпадение.

use re 'debug';
$string = "\t\t\tEntry";
$string =~ s/^(\t*)([^\t]+)/$2/gi;
print "'$string'\n";

Пропуск 1 ..
Соответствие REx "^(\t*)([^\t]+)" против "%t%t%tEntry"
8 <<code>%t%t%tEntry> <>
Совпадение успешно!

Пропуск 2 ..
Соответствие REx "^(\t*)([^\t]+)" против "" (Нет, ничего не найдено)
Слишком короткая строка [regexec_flags] ...
Ошибка совпадения
«Вступление»

...