Какой самый защитный способ петли через линии в файле с Perl?

StackOverflow https://stackoverflow.com/questions/3773917

Вопрос

Я обычно петлю через строки в файле, используя следующий код:

open my $fh, '<', $file or die "Could not open file $file for reading: $!\n";
while ( my $line = <$fh> ) {
  ...
}

Однако, Отвечая на другой вопрос, Эван Кэрролл отредактировал мой ответ, меняя мой while заявление:

while ( defined( my $line = <$fh> ) ) {
  ...
}

Его обоснование было, что если у вас есть линия, которая 0 (Это должно быть последней строкой, иначе у него будет возвращение каретки), то ваш while будет выходить преждевременно, если вы использовали свое заявление ($line будет установлен на "0", и возвращаемая стоимость от назначения, таким образом, также будет "0" который оценивается до false). Если вы проверяете определенную несс, то вы не бежите эту проблему. Это имеет смысл.

Поэтому я попробовал это. Я создал текстовойфиль, чей последний строк 0 без каретки возвращается на нее. Я пробежал через свою петлю, и цикл не выходил преждевременно.

Затем я подумал: «Ага, возможно, ценность на самом деле не 0, может быть, есть что-то еще там, которые прикручивают вещи! "Так я использовал Dump() от Devel::Peek И это то, что он дал мне:

SV = PV(0x635088) at 0x92f0e8
  REFCNT = 1
  FLAGS = (PADMY,POK,pPOK)
  PV = 0X962600 "0"\0
  CUR = 1
  LEN = 80

Это, кажется, говорит мне, что значение на самом деле строку "0", как я получаю аналогичный результат, если я позвоню Dump() на скаляре я явно установил "0" (Единственное отличие находится в поле Лен - из файла Лен составляет 80, тогда как из скалярного лена 8).

Так какая сделка? Почему нет while() петли выйти преждевременно, если я передаю его линию, которая только "0" без возврата каретки? Является ли петлю Эвана на самом деле более оборонительным, или Perl делает что-то безумие внутренне, что означает, что вам не нужно беспокоиться об этих вещах и while() на самом деле только выход, когда вы попадаете eof?

Это было полезно?

Решение

Потому что

 while (my $line = <$fh>) { ... }

на самом деле компилируется до

 while (defined( my $line = <$fh> ) ) { ... }

Возможно, это было необходимо в очень старой версии Perl, но не более! Вы можете увидеть это из бега B :: Darse на вашем скрипте:

>perl -MO=Deparse
open my $fh, '<', $file or die "Could not open file $file for reading: $!\n";
while ( my $line = <$fh> ) {
  ...
}

^D
die "Could not open file $file for reading: $!\n" unless open my $fh, '<', $file;
while (defined(my $line = <$fh>)) {
    do {
        die 'Unimplemented'
    };
}
- syntax OK

Так вы уже добруетесь!

Другие советы

Кстати, это покрыто в разделе операторов ввода / вывода perldoc perlop.:

В скалярном контексте оценка файлов в угловых скобках дает следующую строку из этого файла (Newline, если таковые имеются, включены) или «UNDEF» в конце файла или по ошибке. Когда $ / устанавливается на «UNDEF» (иногда известный как режим File-Slurp), и файл пуст, он возвращает «первый раз, а затем« UNDEF ».

Обычно вы должны назначить возвращенное значение переменной, но есть одна ситуация, когда происходит автоматическое назначение. Если и только в том случае, если входной символ - это единственное, что в пределах условного значения «во время» (даже если она замаскируется как «для (;;)» цикла), значение автоматически назначается глобальной переменной $ _, уничтожая все был там ранее. (Это может показаться не странной вещью для вас, но вы будете использовать конструкцию практически практически в каждом сценарии Perl, который вы пишете.) Переменная $ _ не вспомогательно локализована. Вам придется поставить «локальный $ _;» Перед циклом, если вы хотите, чтобы это произошло.

Следующие строки эквивалентны:

while (defined($_ = <STDIN>)) { print; }
while ($_ = <STDIN>) { print; }
while (<STDIN>) { print; }
for (;<STDIN>;) { print; }
print while defined($_ = <STDIN>);
print while ($_ = <STDIN>);
print while <STDIN>;

Это также ведет себя так же, но позволяет избежать $ _:

while (my $line = <STDIN>) { print $line }

В этих конструкциях петли назначенное значение (будь то назначение автоматическое или явное), затем проверяется, чтобы увидеть, определено ли он. Определенный тест избегает проблем, где строка имеет строковое значение, которое будет относиться как FALES PERL, например, «» или «0» без трейлинга. Если вы действительно имеете в виду для таких ценностей, чтобы завершить петлю, они должны быть проверены на явно:

while (($_ = <STDIN>) ne '0') { ... }
while (<STDIN>) { last unless $_; ... }

В других булевых контекстах "u003Cfilehandle> «Без явного« определенного »теста или сравнения вызывают предупреждение, если« использование предупреждений »Pragma или коммутатор командной строки -w (переменная $ ^ W) существует.

Хотя правильно, что форма while (my $line=<$fh>) { ... } получает скомпилирован к while (defined( my $line = <$fh> ) ) { ... } Рассмотрим множество раз, когда законный прочитанный ценность «0» неверно истолкован, если у вас нет явного defined в петле или тестирование возвращения <>.

Вот несколько примеров:

#!/usr/bin/perl
use strict; use warnings;

my $str = join "", map { "$_\n" } -10..10;
$str.="0";
my $sep='=' x 10;
my ($fh, $line);

open $fh, '<', \$str or 
     die "could not open in-memory file: $!";

print "$sep Should print:\n$str\n$sep\n";     

#Failure 1:
print 'while ($line=chomp_ln()) { print "$line\n"; }:',
      "\n";
while ($line=chomp_ln()) { print "$line\n"; } #fails on "0"
rewind();
print "$sep\n";

#Failure 2:
print 'while ($line=trim_ln()) { print "$line\n"; }',"\n";
while ($line=trim_ln()) { print "$line\n"; } #fails on "0"
print "$sep\n";
last_char();

#Failure 3:
# fails on last line of "0" 
print 'if(my $l=<$fh>) { print "$l\n" }', "\n";
if(my $l=<$fh>) { print "$l\n" } 
print "$sep\n";
last_char();

#Failure 4 and no Perl warning:
print 'print "$_\n" if <$fh>;',"\n";
print "$_\n" if <$fh>; #fails to print;
print "$sep\n";
last_char();

#Failure 5
# fails on last line of "0" with no Perl warning
print 'if($line=<$fh>) { print $line; }', "\n";
if($line=<$fh>) { 
    print $line; 
} else {
    print "READ ERROR: That was supposed to be the last line!\n";
}    
print "BUT, line read really was: \"$line\"", "\n\n";

sub chomp_ln {
# if I have "warnings", Perl says:
#    Value of <HANDLE> construct can be "0"; test with defined() 
    if($line=<$fh>) {
        chomp $line ;
        return $line;
    }
    return undef;
}

sub trim_ln {
# if I have "warnings", Perl says:
#    Value of <HANDLE> construct can be "0"; test with defined() 
    if (my $line=<$fh>) {
        $line =~ s/^\s+//;
        $line =~ s/\s+$//;
        return $line;
    }
    return undef;

}

sub rewind {
    seek ($fh, 0, 0) or 
        die "Cannot seek on in-memory file: $!";
}

sub last_char {
    seek($fh, -1, 2) or
       die "Cannot seek on in-memory file: $!";
}

Я не говорю, что это хорошие формы Perl! Я говорю, что они возможны; Особенно отказ 3,4 и 5. Обратите внимание на неудачу без предупреждения Perl на номере 4 и 5. Первые два имеют свои проблемы ...

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top