Can*_*ice 14 perl defensive-programming while-loop
我通常使用以下代码遍历文件中的行:
open my $fh, '<', $file or die "Could not open file $file for reading: $!\n";
while ( my $line = <$fh> ) {
...
}
Run Code Online (Sandbox Code Playgroud)
然而,在回答另一个问题时,Evan Carroll编辑了我的答案,将我的while陈述改为:
while ( defined( my $line = <$fh> ) ) {
...
}
Run Code Online (Sandbox Code Playgroud)
他的理由是,如果你有一条线0(它必须是最后一条线,否则它将有一个回车)然后while如果你使用我的声明你将会过早退出($line将被设置为"0",并且返回值来自因此,赋值也将"0"被评估为错误).如果检查已定义,则不会遇到此问题.这很有道理.
所以我试了一下.我创建了一个文本文件,其最后一行0没有回车符.我在循环中运行它并且循环没有过早退出.
然后我想,"啊哈,也许这个价值实际上并不0存在,也许那里还有别的事情搞砸了!" 所以我使用Dump()了Devel::Peek,这就是它给了我的东西:
SV = PV(0x635088) at 0x92f0e8
REFCNT = 1
FLAGS = (PADMY,POK,pPOK)
PV = 0X962600 "0"\0
CUR = 1
LEN = 80
Run Code Online (Sandbox Code Playgroud)
这似乎告诉我,该值实际上是字符串"0",因为我得到一个类似的结果,如果我调用Dump()我明确设置的标量"0"(唯一的区别是在LEN字段 - 从文件LEN是80,而从标量LEN是8).
那是什么交易?while()如果我传递一条只有"0"没有回车符的线路,为什么我的循环不会过早退出?Evan的循环实际上是更具防御性,还是Perl在内部做了一些疯狂的事情,这意味着你不需要担心这些事情,while()实际上只有在你击中时退出eof?
Rob*_*t P 18
因为
while (my $line = <$fh>) { ... }
Run Code Online (Sandbox Code Playgroud)
实际上编译为
while (defined( my $line = <$fh> ) ) { ... }
Run Code Online (Sandbox Code Playgroud)
它可能在非常旧版本的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
Run Code Online (Sandbox Code Playgroud)
所以你已经很好了!
Eth*_*her 13
顺便说一句,这在perldoc perlop的I/O运算符部分中有所介绍:
在标量上下文中,评估尖括号中的文件句柄会产生该文件的下一行(包括换行符,如果有的话),或者在文件结尾或出错时的"undef".当$ /设置为"undef"(有时称为文件 - slurp模式)且文件为空时,它将第一次返回'',随后返回"undef".
通常,您必须将返回的值分配给变量,但有一种情况会发生自动分配.当且仅当输入符号是"while"语句条件内的唯一内容时(即使伪装成"for(;;)"循环),该值自动分配给全局变量$ _,破坏任何曾经有过.(这对你来说可能看起来很奇怪,但你几乎在你编写的每个Perl脚本中都会使用该构造.)$ _变量不是隐式本地化的.你必须放一个"本地$ _;" 在循环之前,如果你想要发生这种情况.
以下行是等效的:
Run Code Online (Sandbox Code Playgroud)while (defined($_ = <STDIN>)) { print; } while ($_ = <STDIN>) { print; } while (<STDIN>) { print; } for (;<STDIN>;) { print; } print while defined($_ = <STDIN>); print while ($_ = <STDIN>); print while <STDIN>;这也行为相似,但避免$ _:
Run Code Online (Sandbox Code Playgroud)while (my $line = <STDIN>) { print $line }在这些循环结构中,然后测试指定的值(无论是自动还是显式赋值)以查看它是否已定义.定义的测试避免了line具有字符串值的问题,该字符串值将被Perl视为false,例如""或"0"没有尾随换行符.如果你真的想要这些值来终止循环,那么它们应该被明确地测试:
Run Code Online (Sandbox Code Playgroud)while (($_ = <STDIN>) ne '0') { ... } while (<STDIN>) { last unless $_; ... }在其他布尔上下文中,如果"use warnings"pragma或-w命令行开关($ ^ W变量)生效,则没有显式"已定义"测试或比较的"<filehandle>"会引发警告.
| 归档时间: |
|
| 查看次数: |
5303 次 |
| 最近记录: |