错误处理 #
一、基本错误处理 #
1.1 die函数 #
die 抛出致命错误并终止程序:
perl
my $file = "nonexistent.txt";
open my $fh, "<", $file or die "Cannot open $file: $!";
1.2 warn函数 #
warn 输出警告但不终止程序:
perl
my $file = "nonexistent.txt";
open my $fh, "<", $file or warn "Cannot open $file: $!";
1.3 $!变量 #
$! 包含系统错误信息:
perl
open my $fh, "<", "nonexistent.txt"
or die "Error: $!";
1.4 Carp模块 #
Carp 提供更好的错误报告:
perl
use Carp qw(croak confess cluck);
sub process {
my $file = shift;
open my $fh, "<", $file or croak "Cannot open $file: $!";
}
sub debug_process {
my $file = shift;
open my $fh, "<", $file or confess "Cannot open $file: $!";
}
| 函数 | 说明 |
|---|---|
| croak | die,报告调用位置 |
| confess | die,报告完整调用栈 |
| cluck | warn,报告调用位置 |
| carp | warn,报告调用位置 |
二、eval块 #
2.1 基本用法 #
eval 捕获运行时错误:
perl
my $result = eval {
my $a = 10;
my $b = 0;
$a / $b;
};
if ($@) {
print "Error: $@\n";
} else {
print "Result: $result\n";
}
2.2 eval字符串 #
eval 也可以执行字符串代码(不推荐):
perl
my $code = 'print "Hello\n"';
eval $code;
if ($@) {
print "Error: $@\n";
}
2.3 安全使用eval #
perl
sub safe_divide {
my ($a, $b) = @_;
my $result = eval { $a / $b };
if ($@) {
warn "Division error: $@";
return undef;
}
return $result;
}
2.4 eval块最佳实践 #
perl
my $result;
eval {
$result = some_operation();
1;
} or do {
my $error = $@;
handle_error($error);
};
三、Try::Tiny模块 #
3.1 基本用法 #
Try::Tiny 提供更清晰的异常处理:
perl
use Try::Tiny;
try {
my $result = 10 / 0;
} catch {
warn "Error: $_";
} finally {
print "Cleanup\n";
};
3.2 返回值 #
perl
use Try::Tiny;
my $result = try {
return some_operation();
} catch {
warn "Error: $_";
return undef;
};
3.3 完整示例 #
perl
use Try::Tiny;
sub process_file {
my $file = shift;
my $content = try {
open my $fh, "<", $file or die "Cannot open: $!";
local $/;
my $data = <$fh>;
close $fh;
return $data;
} catch {
warn "Failed to read $file: $_";
return undef;
};
return $content;
}
四、自定义异常 #
4.1 使用die抛出异常 #
perl
sub withdraw {
my ($balance, $amount) = @_;
if ($amount > $balance) {
die "Insufficient funds: balance=$balance, requested=$amount";
}
return $balance - $amount;
}
eval {
withdraw(100, 150);
};
if ($@) {
print "Transaction failed: $@\n";
}
4.2 异常对象 #
perl
package MyException;
use overload '""' => \&stringify;
sub new {
my ($class, %args) = @_;
return bless \%args, $class;
}
sub message { shift->{message} }
sub code { shift->{code} }
sub stringify {
my $self = shift;
return "Error [$self->{code}]: $self->{message}";
}
1;
使用:
perl
use Try::Tiny;
try {
die MyException->new(
message => "Something went wrong",
code => 500,
);
} catch {
if (ref $_ eq 'MyException') {
print "Custom error: $_\n";
} else {
print "Unknown error: $_\n";
}
};
五、错误处理模式 #
5.1 返回错误码 #
perl
sub divide {
my ($a, $b) = @_;
return (undef, "Division by zero") if $b == 0;
return ($a / $b, undef);
}
my ($result, $error) = divide(10, 0);
if ($error) {
print "Error: $error\n";
} else {
print "Result: $result\n";
}
5.2 使用undef表示错误 #
perl
sub read_config {
my $file = shift;
open my $fh, "<", $file or return;
local $/;
my $content = <$fh>;
close $fh;
return $content;
}
my $config = read_config("config.txt");
unless (defined $config) {
die "Failed to read config";
}
5.3 回调错误处理 #
perl
sub process {
my (%args) = @_;
my $on_success = $args{on_success} // sub {};
my $on_error = $args{on_error} // sub { die @_ };
my $result = eval { do_something() };
if ($@) {
$on_error->($@);
} else {
$on_success->($result);
}
}
process(
on_success => sub { print "Success: $_[0]\n" },
on_error => sub { warn "Error: $_[0]" },
);
六、日志记录 #
6.1 简单日志 #
perl
sub log_error {
my $msg = shift;
my $timestamp = localtime();
print STDERR "[$timestamp] ERROR: $msg\n";
}
sub log_info {
my $msg = shift;
my $timestamp = localtime();
print STDERR "[$timestamp] INFO: $msg\n";
}
eval {
log_info("Starting operation");
some_operation();
log_info("Operation completed");
};
if ($@) {
log_error($@);
}
6.2 使用Log::Log4perl #
perl
use Log::Log4perl;
Log::Log4perl->init(\<<'END');
log4perl.rootLogger=DEBUG, Screen
log4perl.appender.Screen=Log::Log4perl::Appender::Screen
log4perl.appender.Screen.layout=PatternLayout
log4perl.appender.Screen.layout.ConversionPattern=%d %p %m %n
END
my $logger = Log::Log4perl->get_logger();
$logger->debug("Debug message");
$logger->info("Info message");
$logger->warn("Warning message");
$logger->error("Error message");
$logger->fatal("Fatal message");
七、实践练习 #
练习1:安全文件操作 #
perl
#!/usr/bin/perl
use strict;
use warnings;
use Try::Tiny;
use v5.10;
sub safe_read_file {
my $file = shift;
my $content = try {
open my $fh, "<", $file or die "Cannot open: $!";
local $/;
my $data = <$fh>;
close $fh;
return $data;
} catch {
warn "Error reading $file: $_";
return undef;
};
return $content;
}
my $content = safe_read_file("test.txt");
if (defined $content) {
say "Read " . length($content) . " bytes";
}
练习2:数据库操作 #
perl
#!/usr/bin/perl
use strict;
use warnings;
use Try::Tiny;
use v5.10;
sub db_query {
my ($sql, @params) = @_;
my $result = try {
die "Connection failed" unless connect_db();
my $sth = prepare($sql);
$sth->execute(@params);
return $sth->fetchall_arrayref();
} catch {
log_error($_);
return undef;
};
return $result;
}
sub log_error {
my $msg = shift;
my $time = localtime();
print STDERR "[$time] ERROR: $msg\n";
}
sub connect_db { return 1; }
sub prepare { return bless {}, 'STH'; }
package STH;
sub execute {}
sub fetchall_arrayref { return []; }
练习3:配置验证 #
perl
#!/usr/bin/perl
use strict;
use warnings;
use Try::Tiny;
use v5.10;
sub validate_config {
my $config = shift;
my @errors;
try {
die "Missing 'host'" unless $config->{host};
die "Invalid 'port'" unless $config->{port} && $config->{port} > 0;
die "Missing 'database'" unless $config->{database};
} catch {
push @errors, $_;
};
return @errors ? \@errors : undef;
}
my $config = {
host => "localhost",
port => 3306,
database => "mydb",
};
my $errors = validate_config($config);
if ($errors) {
say "Validation errors:";
say " $_" foreach @$errors;
} else {
say "Config is valid";
}
八、总结 #
本章学习了:
- die和warn函数
- eval块捕获错误
- Try::Tiny模块
- 自定义异常
- 错误处理模式
- 日志记录
下一章将学习CPAN模块管理。
最后更新:2026-03-27