Perl - 错误处理



执行和错误总是同时存在。如果你打开一个不存在的文件,那么如果你没有正确处理这种情况,你的程序就被认为是低质量的。

如果发生错误,程序将停止运行。因此,适当的错误处理用于处理程序执行过程中可能发生的各种类型的错误,并采取相应的措施,而不是完全停止程序。

你可以通过多种不同的方式识别和捕获错误。在 Perl 中捕获错误然后正确处理它们非常容易。这里有一些可以使用的的方法。

if 语句

当你需要检查语句的返回值时,if 语句是显而易见的选择;例如:

if(open(DATA, $file)) {
   ...
} else {
   die "Error: Couldn't open the file - $!";
}

这里变量 $! 返回实际的错误消息。或者,在有意义的情况下,我们可以将语句简化为一行;例如:

open(DATA, $file) || die "Error: Couldn't open the file $!";

unless 函数

unless 函数与 if 的逻辑相反:语句可以完全绕过成功状态,只有在表达式返回 false 时才执行。例如:

unless(chdir("/etc")) {
   die "Error: Can't change directory - $!";
}

当你想只在表达式失败时引发错误或替代方案时,unless 语句最好用。当用于单行语句时,该语句也有意义:

die "Error: Can't change directory!: $!" unless(chdir("/etc"));

只有在 chdir 操作失败时我们才会 die,而且读起来很流畅。

三元运算符

对于非常短的测试,可以使用条件运算符 ?:

print(exists($hash{value}) ? 'There' : 'Missing',"\n");

这里不太清楚我们试图实现什么,但效果与使用 ifunless 语句相同。当你想在一个表达式或语句中快速返回两个值中的一个时,条件运算符最好用。

warn 函数

warn 函数只发出警告,一条消息将打印到 STDERR,但不会采取进一步的措施。因此,如果你只想为用户打印警告并继续执行其余操作,它更有用:

chdir('/etc') or warn "Can't change directory";

die 函数

die 函数的工作方式与 warn 类似,只是它还会调用 exit。在一个普通的脚本中,这个函数的效果是立即终止执行。如果程序中出现错误,继续执行是没有意义的,你应该使用此函数:

chdir('/etc') or die "Can't change directory";

模块中的错误

我们应该能够处理两种不同的情况:

  • 报告模块中的错误,该错误引用模块的文件名和行号——这在调试模块时很有用,或者当你想引发与模块相关的而不是与脚本相关的错误时。

  • 报告模块中的错误,该错误引用调用者的信息,以便你可以调试导致错误的脚本中的行。以这种方式引发的错误对最终用户很有用,因为它们突出了与调用脚本的原始行相关的错误。

当从模块中调用时,warndie 函数的工作方式与你预期略有不同。例如,简单的模块:

package T;

require Exporter;
@ISA = qw/Exporter/;
@EXPORT = qw/function/;
use Carp;

sub function {
   warn "Error in module!";
}
1;

当从如下脚本调用时:

use T;
function();

它将产生以下结果:

Error in module! at T.pm line 9.

这或多或少是你可能预期的,但不一定是你想看到的。从模块程序员的角度来看,这些信息很有用,因为它有助于指出模块本身中的错误。对于最终用户来说,提供的信息相当无用,对于除经验丰富的程序员以外的所有人来说,它完全没有意义。

解决此类问题的方案是 Carp 模块,它提供了一种简化的方法来报告模块中的错误,这些错误返回有关调用脚本的信息。Carp 模块提供四个函数:carp、cluck、croak 和 confess。这些函数将在下面讨论。

carp 函数

carp 函数是 warn 的基本等价物,它将消息打印到 STDERR,而不会实际退出脚本并打印脚本名称。

package T;

require Exporter;
@ISA = qw/Exporter/;
@EXPORT = qw/function/;
use Carp;

sub function {
   carp "Error in module!";
}
1;

当从如下脚本调用时:

use T;
function();

它将产生以下结果:

Error in module! at test.pl line 4

cluck 函数

cluck 函数是一种增强的 carp,它遵循相同的基本原理,但还会打印导致调用该函数的所有模块的堆栈跟踪,包括原始脚本的信息。

package T;

require Exporter;
@ISA = qw/Exporter/;
@EXPORT = qw/function/;
use Carp qw(cluck);

sub function {
   cluck "Error in module!";
}
1;

当从如下脚本调用时:

use T;
function();

它将产生以下结果:

Error in module! at T.pm line 9
   T::function() called at test.pl line 4

croak 函数

croak 函数等同于 die,只是它向上报告一个级别的调用者。与 die 一样,此函数还会在将错误报告到 STDERR 后退出脚本:

package T;

require Exporter;
@ISA = qw/Exporter/;
@EXPORT = qw/function/;
use Carp;

sub function {
   croak "Error in module!";
}
1;

当从如下脚本调用时:

use T;
function();

它将产生以下结果:

Error in module! at test.pl line 4

与 carp 一样,关于根据 warn 和 die 函数包含行和文件信息的相同基本规则适用。

confess 函数

confess 函数类似于 cluck;它调用 die,然后打印一直到原始脚本的堆栈跟踪。

package T;

require Exporter;
@ISA = qw/Exporter/;
@EXPORT = qw/function/;
use Carp;

sub function {
   confess "Error in module!";
}
1;

当从如下脚本调用时:

use T;
function();

它将产生以下结果:

Error in module! at T.pm line 9
   T::function() called at test.pl line 4
广告