通过Perl的文件中的线路循环的最防御方法是什么?
-
04-10-2019 - |
题
我通常使用以下代码在文件中的行循环:
open my $fh, '<', $file or die "Could not open file $file for reading: $!\n";
while ( my $line = <$fh> ) {
...
}
然而, 在回答另一个问题时, 埃文·卡罗尔(Evan Carroll) 编辑了我的答案,改变了我的 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"
(唯一的区别是在LEN字段中 - 从文件中为80,而从标量为8的是8)。
那怎么办?为什么没有我的 while()
循环过早退出,如果我通过了一条只有一条只有 "0"
没有马车返回?是埃文的循环实际上更具防御力,还是在内部做疯狂的事情,这意味着您不必担心这些事情,并且 while()
实际上只有在您击中时才退出 eof
?
解决方案
因为
while (my $line = <$fh>) { ... }
实际上汇编到
while (defined( my $line = <$fh> ) ) { ... }
在非常古老的Perl版本中可能是必要的,但不再需要!您可以从脚本上运行B :: Deparse看到这一点:
>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
所以你已经好了!
其他提示
顺便说一句,这在I/O操作员部分中涵盖 perldoc perlop:
在标量上下文中,评估角度括号中的文件打架会从该文件(如果有的话)或错误的文件或错误时产生下一行(Newline(如果有))或“ Undef”。当$/设置为“ undef”(有时称为文件弹性模式)并且文件为空时,它首次返回',然后是“ undef”。
通常,您必须将返回的值分配给变量,但是在某种情况下发生自动分配。当且仅当输入符号是“ while”语句的条件(即使被伪装成“(;;)循环)的唯一内容时,该值会自动分配给全局变量$ _,销毁任何内容以前在那里。 (这对您来说似乎是一件奇怪的事情,但是您几乎在您编写的每个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 }
在这些循环构造中,然后测试了分配的值(分配是自动的还是显式),以查看是否定义了它。定义的测试避免了线路值将被视为false的问题,例如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。请注意,第4和5号的失败没有警告。前两个有自己的问题...