我是 Perl 的新手,但我对 C 有一点了解。我在我们的一个课堂笔记中看到了这个片段:
$STUFF = "C:/scripts/stuff.txt";
open STUFF or die "Cannot open $STUFF for read: $!";
print "Line $. is: $_" while (<STUFF>);
为什么是while
afterprint
语句?它有什么作用?
我是 Perl 的新手,但我对 C 有一点了解。我在我们的一个课堂笔记中看到了这个片段:
$STUFF = "C:/scripts/stuff.txt";
open STUFF or die "Cannot open $STUFF for read: $!";
print "Line $. is: $_" while (<STUFF>);
为什么是while
afterprint
语句?它有什么作用?
这是一样的
while (<STUFF>) {
print "Line $. is : $_";
}
之所以这样写,是因为它更简单、更紧凑。一般来说,这是 Perl著名的用于条件和循环的“语句修饰符”形式。
其他答案已经解释了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;
}
您还应该查看strict
andwarnings
编译指示并查看更新的三参数形式open
. 我刚刚注意到你是“未知(谷歌) ”,这意味着你很可能永远不会回来。我想我至少有一天的打字练习。
以下代码片段完全相同,只是编写相同内容的不同方式:
print "Line $. is : $_" while (<STUFF>);
while (<STUFF>) {
print "Line $. is : $_";
}
它的作用是在循环中的每次迭代,Perl 从文件中读取一行文本STUFF
并将其放入特殊变量中$_
(这就是尖括号的作用)。然后循环体打印如下行:
第 1 行是:测试 第 2 行是:文件
特殊变量$.
是从文件中读取的最后一行的行号,并且$_
是由尖括号运算符设置的该行的内容。
在 print 之后放置while
,使该行读起来几乎像普通英语一样。
它还强调了print
而不是while
。而且你不需要大括号:{ ... }
它也可以与if
和一起使用unless
,例如,
print "Debug: foobar=$foobar\n" if $DEBUG;
print "Load file...\n" unless $QUIET;
我冒昧地重写了您的代码段。
在我建议的代码下方是一个流氓画廊,其中包含您可能在野外看到的非最佳方法。
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";
}
请注意,while
如果它是单行语句,则该语句只能跟随您的循环体。如果你的循环体跨越多行,那么你while
必须在它之前。
它与以下相同:
while (<STUFF>) { print "Line $. is: $_"; }