Что означает некоторое время после печати в Perl? - PullRequest
5 голосов
/ 10 июля 2009

Я новичок в Perl, но немного знаю C, хотя. Я наткнулся на этот фрагмент в одной из наших заметок:

$STUFF = "C:/scripts/stuff.txt";

open STUFF or die "Cannot open $STUFF for read: $!";

print "Line $. is: $_" while (<STUFF>);

Почему while после оператора print? Что это делает?

Ответы [ 7 ]

11 голосов
/ 10 июля 2009

Это так же, как

while (<STUFF>) {
    print "Line $. is : $_";
}

Написано так, потому что это проще и компактнее. В общем, это известная форма Perl (in) для «условного модификатора оператора» для условных выражений и циклов.

7 голосов
/ 10 июля 2009

Другие ответы объяснили форму модификатора оператора цикла while. Однако здесь происходит много другой магии. В частности, скрипт использует три специальные переменные Perl. Два из них ($_ и $!) очень распространены; другой ($.) достаточно распространен. Но они все стоит знать.

Когда вы запускаете while <$fh> для открытого дескриптора файла, Perl автоматически пробегает файл, строка за строкой, пока не достигнет EOF. В каждом цикле текущая строка установлена ​​на $_ без каких-либо действий. Итак, эти два одинаковы:

while (<$fh>) { # something }
while (defined($_ = <$fh>)) { # something }

См. perldoc perlop, раздел об операторах ввода / вывода. (Некоторые люди считают это слишком магическим, поэтому вместо них они используют while (my $line = <$fh>). Это дает вам $line для каждой строки, а не $_, что является более понятным именем переменной, но требует большего набора текста. Каждому свое.)

$! содержит значение системной ошибки (если она установлена). См. perldoc perlvar, раздел $OS_ERROR, для получения дополнительной информации о том, как и когда использовать это.

$. содержит номер строки. См. perldoc perlvar, раздел $NR. Эта переменная может быть на удивление хитрой. Он не обязательно будет содержать номер строки файла, который вы сейчас читаете. Пример:

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

while (<>) {
    print "$ARGV: $.\n";
}

Если вы сохраните это как lines и запустите его как perl lines file1 file2 file3, то Perl будет считать строки прямо через файл1, файл2 и файл3. Вы можете видеть, что Perl знает, из какого файла он читает (он находится в $ ARGV; имена файлов будут правильными), но он не автоматически сбрасывает нумерацию строк в конце каждого файла. Я упоминаю об этом, так как меня это поведение кусало не раз, пока я наконец не получил его через свой (толстый) череп. Вы можете сбросить нумерацию для отслеживания отдельных файлов следующим образом:

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

while (<>) {
    print "$ARGV: $.\n";
}
continue {
    close ARGV if eof;
}

Вы также должны проверить strict и warnings прагм и взглянуть на более новую форму с тремя аргументами open. Я только что заметил, что вы " unknown (google) ", что означает, что вы, вероятно, никогда не вернетесь. Думаю, я получил дневную практику по крайней мере за день.

4 голосов
/ 10 июля 2009

Помещение while после печати приводит к тому, что строка читается почти как обычный английский.

Он также делает акцент на print вместо while. И вам не нужны фигурные скобки: { ... }

Может также использоваться с if и unless, например,

print "Debug: foobar=$foobar\n" if $DEBUG;

print "Load file...\n" unless $QUIET;
4 голосов
/ 10 июля 2009

Следующие фрагменты в точности эквивалентны, просто разные способы написания одного и того же:

print "Line $. is : $_" while (<STUFF>);

while (<STUFF>) {
    print "Line $. is : $_";
}

Для каждой итерации цикла Perl читает одну строку текста из файла STUFF и помещает ее в специальную переменную $_ (это то, что делают угловые скобки). Затем тело цикла печатает строки вроде:

Line 1 is : test
Line 2 is : file

Специальная переменная $. - это номер строки последней строки, прочитанной из файла, а $_ - это содержимое этой строки, установленное оператором угловой скобки.

3 голосов
/ 10 июля 2009

Я взял на себя смелость переписать ваш фрагмент так же, как и я.

Ниже моего предлагаемого кода находится галерея мошенников с неоптимальными методами, которые вы можете увидеть в дикой природе.

use strict;
use warnings;

my $stuff_path = 'c:/scripts/stuff.txt';

open (my $stuff, '<', $stuff_path)
    or die "Cannot open'$stuff_path' for read : $!\n";

# My preferred method to loop over a file line by line:
# while loop with explicit variable
while( my $line = <$stuff> ) {
    print "Line $. is : $line\n";
}

Вот другие методы, которые вы можете увидеть. Каждый из них мог бы заменить цикл while в моем примере выше.

# while statement modifier
# - OK, but less clear than explicit code above.
print "Line $. is : $_" while <$stuff>;

# while loop
# - OK, but if I am operating on $_ explicitly, I prefer to use an explicit variable.
while( <$stuff> ) {
   print "Line $. is : $_";
}

# for statement modifier 
# - inefficient
# - loads whole file into memory
print "Line $. is : $_" for <$stuff>;

# for loop - inefficient
# - loads whole file into memory;
for( <$stuff> ) {
    print "Line $. is : $_\n";
}

# for loop with explicit variable
# - inefficient
# - loads whole file into memory;
for my $line ( <$stuff> ) {
    print "Line $. is : $line\n";
}

# Exotica -

# map and print
# - inefficient
# - loads whole file into memory
# - stores complete output in memory
print map "Line $. is : $_\n", <$stuff>;

# Using readline rather than <>
# - Alright, but overly verbose
while( defined (my $line = readline($stuff) ) {
    print "Line $. is : $line\n";    
}

# Using IO::Handle methods on a lexical filehandle
# - Alright, but overly verbose
use IO::Handle;   
while( defined (my $line = $stuff->readline) ) {
    print "Line $. is : $line\n";    
}
1 голос
/ 10 июля 2009

Обратите внимание, что оператор while может следовать только вашему телу цикла, если он содержит одну строку. Если ваше тело цикла пересекает несколько строк, то вашему while должно предшествовать это.

0 голосов
/ 10 июля 2009

То же, что и:

while (<STUFF>) { print "Line $. is: $_"; }
...