我在大学学计算机科学的时候,学了很多关于如何写程序的,但是据我所知,至今没人教我们怎么调试。 我们听说过这个创造新事物的美好世界,但是没人告诉我们大多数时间里,我们需要理解别人的代码。
事实证明在写程序时,我们花费很多时间在尝试理解我们(或者他人)所写的代码以及为什么工作异常。 这时间远多过首次把代码写出来。
什么是调试?
执行程序前,一切看起来很好。
执行程序后,某些东西变成出乎意料、不正确的状态。
任务就是找出在哪一点出了什么问题并改正它。
什么是编程,什么是bug?
基本上,编程是通过在变量里搬动数据来一点点改变世界。
在程序的每一步,我们改变程序中某变量的一些数据或者“现实世界”的一些东西。(比如磁盘里或者屏幕上)
当你写程序时,你每一步安排:什么值应该赋给哪个变量。
bug就是当你认为你把值X放入某个变量,而实际上值Y被放入了。
在某一点,通常是程序结束,你会发现它因为程序打印了不正确的值。
程序执行时,它可能导致出现警告或者程序异常终止。
怎么调试?
最直接的调试办法是运行程序,在每一步检查是否所有的变量包含了期望的值。你可以通过使用调试器或者在程序中嵌入print语句后,来检查输出。
Perl带了很强大的命令行调试器。我建议学一下,不过刚开始可能会比较恐怖。 我准备了一个视频来演示Perl内置调试器的基本命令。
集成开发环境,诸如Komodo,Eclipse和Padre, the Perl IDE 带了一个图形界面的调试器。有时间我也会准备一个他们的视频。
打印语句
很多人还在用老掉牙的办法去在代码里加入打印语句。
如果一种语言编译和构建很花时间,打印语句就是一个很坏的办法去调试代码。 Perl不是这样,即便是很大的应用程序也可以在几秒钟编译并开始运行。
添加打印语句时,需要记住在值周围加上分隔符。这样可以发现值之前或之后有空白可能导致问题的情况。 不加分隔符的话,很难发现他们:
标量值可以如此打印:
print "<$file_name>\n";
这里小于号和大于号只是使读者易于辨识变量的真实内容:
<path/to/file
>
如果打印如上结果,你会很快发现$file_name变量后有个拖尾的新行符。或许是你忘了调用chomp。
复杂数据结构
我们甚至还没学标量,但是让我来往前跳一下来为您展示如何打印复杂数据结构的内容。 如果你在Perl教程中读到这一部分,你可以希望跳到下一章节,之后再回来。现在这不是太大问题。 否则,继续读下去。
对于复杂数据结构(引用,数组和哈希),你可以使用Data::Dumper
use Data::Dumper qw(Dumper);
print Dumper \@an_array;
print Dumper \%a_hash;
print Dumper $a_reference;
这会打印类似如下内容,它有助于了解变量的内容,但是只是显示了一个通用的变量名比如$VAR1
和$VAR2
。
$VAR1 = [
'a',
'b',
'c'
];
$VAR1 = {
'a' => 1,
'b' => 2
};
$VAR1 = {
'c' => 3,
'd' => 4
};
我建议像这样加一些代码打印出变量名:
print '@an_array: ' . Dumper \@an_array;
这将得出:
@an_array: $VAR1 = [
'a',
'b',
'c'
];
或者像这样使用Data::Dumper:
print Data::Dumper->Dump([\@an_array, \%a_hash, $a_reference],
[qw(an_array a_hash a_reference)]);
得出
$an_array = [
'a',
'b',
'c'
];
$a_hash = {
'a' => 1,
'b' => 2
};
$a_reference = {
'c' => 3,
'd' => 4
};
还有更漂亮的办法打印数据结构,但是这里Data::Dumper
已经足够满足我们的需求,而且每个perl版本都预装了。
我们以后会讨论其他方法。