5

我是 Perl 的新手,但我对 C 有一点了解。我在我们的一个课堂笔记中看到了这个片段:

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

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

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

为什么是whileafterprint语句?它有什么作用?

4

7 回答 7

11

这是一样的

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

之所以这样写,是因为它更简单、更紧凑。一般来说,这是 Perl著名的用于条件和循环的“语句修饰符”形式。

于 2009-07-10T11:29:00.580 回答
7

其他答案已经解释了while循环的语句修饰符形式。然而,这里还有很多其他的魔法。特别是,该脚本依赖于 Perl 的三个特殊变量。其中两个 ($_$!) 非常常见;另一个 ( $.) 是相当常见的。但他们都值得知道。

当您while <$fh>在打开的文件句柄上运行时,Perl 会自动逐行遍历文件,直到找到EOF. 在每个循环中,当前行设置为$_无需您执行任何操作。所以这两个是一样的:

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

请参阅perldoc perlop有关 I/O 操作符的部分。(有些人觉得这太神奇,所以他们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 将直接计算通过 file1、file2 和 file3 的行数。您可以看到 Perl 知道它正在读取哪个文件(它在 $ARGV 中;文件名将是正确的),但它不会在每个文件的末尾自动为您重置行号。我提到这一点是因为我不止一次被这种行为所困扰,直到我终于通过我的(厚)头骨得到了它。您可以通过以下方式重置编号以跟踪单个文件:

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

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

您还应该查看strictandwarnings编译指示并查看更新的三参数形式open. 我刚刚注意到你是“未知(谷歌) ”,这意味着你很可能永远不会回来。我想我至少有一天的打字练习。

于 2009-07-10T12:04:24.613 回答
4

以下代码片段完全相同,只是编写相同内容的不同方式:

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

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

它的作用是在循环中的每次迭代,Perl 从文件中读取一行文本STUFF并将其放入特殊变量中$_(这就是尖括号的作用)。然后循环体打印如下行:

第 1 行是:测试
第 2 行是:文件

特殊变量$.是从文件中读取的最后一行的行号,并且$_是由尖括号运算符设置的该行的内容。

于 2009-07-10T11:30:00.530 回答
4

在 print 之后放置while,使该行读起来几乎像普通英语一样。

它还强调了print而不是while。而且你不需要大括号:{ ... }

它也可以与if和一起使用unless,例如,

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

print "Load file...\n" unless $QUIET;
于 2009-07-10T11:35:35.670 回答
3

我冒昧地重写了您的代码段。

在我建议的代码下方是一个流氓画廊,其中包含您可能在野外看到的非最佳方法。

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";    
}
于 2009-07-10T18:48:13.517 回答
1

请注意,while如果它是单行语句,则该语句只能跟随您的循环体。如果你的循环体跨越多行,那么你while必须在它之前。

于 2009-07-10T11:43:25.653 回答
0

它与以下相同:

while (<STUFF>) { print "Line $. is: $_"; }
于 2009-07-10T11:29:27.053 回答