Perl 文件读写
很多Perl程序是用来处理文本文件(如配置文件或者日志文件),所以为了让我们的知识可以用起来,一开始就学习文件处理是很重要的。
首先来看一下怎么写文件。
在写之前,你需要打开文件,让操作系统(Windows,Linux,OSX等)给你的程序开启一个与文件“对话”的通道。为了这样做,Perl提供了一个语法有些奇怪的open函数。
use strict; use warnings; my $filename = 'report.txt'; open(my $fh, '>', $filename) or die "Could not open file '$filename' $!"; print $fh "My first report generated by perl\n"; close $fh; print "done\n";
这是个好例子,我们在后面会用到它,而现在先来看一个更简单的例子:
简单示例
use strict; use warnings; open(my $fh, '>', 'report.txt'); print $fh "My first report generated by perl\n"; close $fh; print "done\n";
简单解释一下。open 函数可以传入3个参数。
第一个,$fh,是个在 open() 调用中定义的标量。我们也可以在函数调用之前就定义它,不过,即便开始的时候你觉得别扭,选择在调用过程中定义这个变量会让代码更清晰一些。第二个参数定义了文件的打开方式。本例中的大于号(>)表示打开文件是为了后面的写操作。第三个参数是文件路径。
当调用这个函数的时候,它在$fh变量中设定了一个特殊的记号,称为文件句柄。我们并不关心这个变量的内容,仅仅在后面使用它。需要记住的是:文件的内容仍然在磁盘上,并不 在$fh变量中。
一旦打开了文件我们就可以在perl()的语句中使用文件句柄$fh。它看起来跟教程其它部分的print()很像,但是这里第一个参数是文件句柄,并且后面没有(!)逗号。
上面的print()会在文件里写入文本。
后一行关闭了文件句柄。严格来说这在Perl并不是必须的。Perl会在该变量超出作用域的时候自动并且适当的关闭所有的文件句柄。(至少在脚本的最后是这么做的) 无论如何, 显式地关闭文件是一种很好的实践习惯。
最后一行print "done\n"是为了让下个例子更加清晰:
错误处理
我们可以再举上边的例子,但是这次使用一个不存在的文件路径。例如:
open(my $fh, '>', 'some_strange_name/report.txt');
如果现在执行脚本,你会看到这样的错误信息:
print() on closed file-handle $fh at ... done
事实上,这只是一个警告; 脚本会继续执行,这也是我们在屏幕上看到"done"被打印出来的原因。
进一步来说,只有在显式地使用use warnings语句的时候才会有警告信息。 你可以尝试一下注释掉use warnings,来验证:创建文件失败的时候,脚本现在是保持安静的。(不会提示警告信息) 这样的话, 在用户或者老板抱怨之前你都不会注意到它。
不过这仍是一个问题:我们尝试着去打开文件,尽管失败了,但是仍然想要往里print()数据。
我们最好在进一步处理之前先检查一下open()是否成功。
幸运的是,open() 的函数调用本身会返回 TRUE(成功), FALSE(失败) , 所以我们可以这么写:
Open or die
open(my $fh, '>', 'some_strange_name/report.txt') or die;
这是标准的open or die习语。 在Perl中很常见。
die 是一个函数调用,它会抛出一个异常并停止执行脚本。
"open or die" 是一个逻辑表达式。正如你从教程的前半部分看到的那样,"or" 在Perl中是短路执行的。这意味着,如果左半部分是TRUE,我们就能知道整个表达式是TRUE,式子右半部分是不执行的。相反,如果左半部分是FALSE,那么右半部分会执行,而且它的结果就是整个表达式的结果。
在本例中,我们使用短路特性来写表达式。
如果open()成功执行, 返回TRUE, 那么右边部分不会执行。 脚本会到下一行。
如果open()执行失败, 返回FALSE。 那么or右边部分会执行。 它会抛出异常并退出脚本。
在上边的例子中我们并不关心逻辑表达式实际的返回值, 只是使用它的"副作用"。
如果你尝试执行上面更改的脚本,会得到错误信息:
Died at ...
并不会打印"done"。
更好的错误报告
除了不加参数的调用die以外,我们也可以附加一些说明信息。
open(my $fh, '>', 'some_strange_name/report.txt') or die "Could not open file 'some_strange_name/report.txt'";
将会输出
Could not open file 'some_strange_name/report.txt' ...
这会好一些,但是如果有人会尝试把文件的路径修改正确 ...
open(my $fh, '>', 'correct_directory_with_typo/report.txt') or die "Could not open file 'some_strange_name/report.txt'";
...而你得到的仍然还是原来的错误信息。这是因为他们只是在open()调用中进行了修改,却忘了改传给die的参数。
用一个变量来代替文件名,可能会更好点:
my $filename = 'correct_directory_with_typo/report.txt'; open(my $fh, '>', $filename) or die "Could not open file '$filename'";
现在我们能获得正确的错误信息,不过仍然不知道为什么会失败。为了进一步探究,我们可以使用Perl的内置变量$!来打印出操作系统的错误提示:
my $filename = 'correct_directory_with_typo/report.txt'; open(my $fh, '>', $filename) or die "Could not open file '$filename' $!";
这样会输出
Could not open file 'some_strange_name/report.txt' No such file or directory ...
好多了吧。
现在我们可以回到最初的例子。
大于?
在调用open时使用的大于号可能有点不太清晰,但是如果你熟悉命令行的重定向的话,这应该对你不成问题。不然,可以把它想象成指示数据流方向(流向右侧的文件)的箭头。
非拉丁字符?
在需要处理非ASCII字符的情况下, 你可能想要使用UTF-8来存储它们。这样的话,你需要告诉Perl:你正在使用UTF-8编码打开文件。
open(my $fh, '>:encoding(UTF-8)', $filename) or die "Could not open file '$filename'";
Published on 2013-04-21