Perl的IO操做(1):文件句柄

文件句柄

文件句柄用来对应要操做的文件系统中的文件,这么说不太严谨,但比较容易理解。首先为要打开的文件绑定文件句柄(称为打开文件句柄),而后在后续的操做中都经过文件句柄来操做对应的文件,最后关闭文件句柄。html

如不理解文件句柄的概念,可将文件句柄看做Linux中文件描述符的概念(固然,它们是不一样的,Perl的文件句柄在层次上对应于Linux中的标准IO流)。例如特殊的STDIN、STDOUT、STDERR就是perl中预约义好的文件句柄,分别表示标准输入、标准输出、标准错误,要将它们对应到Linux上的话,它们是默认的文件描述符fd=0、fd=1和fd=2的字符串描述形式,而这几个文件描述符分别对应文件系统中的/dev/stdin、/dev/stdout和/dev/stderr设备文件。也就是说,Linux上的perl中的文件句柄STDIN、STDOUT和STDERR默认关联的文件是/dev/stdin、/dev/stdout和/dev/stderr。java

若是还不理解文件句柄,就把它想象成通道:perl程序中IO操做和磁盘上文件的通道。例如,print语句将数据经过某个通道输出到对应的文件中。shell

文件句柄和文件描述符函数

实际上,文件句柄和文件描述符是有区别的。文件描述符是一个数值,表明操做系统所使用的裸数据流,文件描述符是文件句柄的核心,文件句柄能够看做是文件描述符的更高一层次的封装,好比提供了和描述符有关数据流的输入、输出的buffer缓冲。也就是说,文件句柄比文件描述符多一些额外的功能。编码

咱们能够为任意要操做的文件定义一个文件句柄。一般,使用大写字母做为文件句柄的名称。操作系统

例如,下面打开一个文件句柄LOG,这个文件句柄对应的文件是/tmp/a.log,操做模式是追加写入(和shell中的追加剧定向是相同的意思)。而后向LOG文件句柄中输入一段数据,最后关闭文件句柄。命令行

open LOG,">>/tmp/a.log";
print LOG "haha, hello world";
close LOG;

通常来讲,打开了文件句柄后,在操做完成后要关闭文件句柄以便节省操做系统"打开文件数量限制"的资源。但perl有时候比较智能,会在某些时候自动帮咱们关掉文件句柄。并且,当打开一个文件句柄后,再次去打开这个文件句柄时,perl会先关闭这个文件句柄再打开这个文件句柄,这称为"文件句柄的reopen",它只是隐式地关闭并从新打开,perl并不认为中间涉及了关闭操做(例如reopen时行号不会重置)。rest

打开文件句柄

要打开文件句柄,使用open函数。open函数的功能其实很丰富,若有须要,可去官方手册查看:http://perldoc.perl.org/functions/open.htmlcode

打开(open)文件是有目的的:为了读取?为了写入?为了追加写入?这是操做模式。在open文件时,须要指明操做模式。此外,还要给定要关联的文件路径,路径能够是绝对路径,也能够是相对当前perl程序的相对路径。htm

另外注意,文件句柄需惟一,不能出现重名。

例如:

open LOG1,">","/tmp/a.log";   # 以覆盖写入的方式打开文件/tmp/a.log
open LOG2,">>","/tmp/a.log";  # 以追加写入的方式打开文件/tmp/a.log
open LOG3,"<","/tmp/a.log";   # 打开/tmp/a.log文件,以提供输入源
open LOG4,"/tmp/a.log";       # 等价于上面的输入,默认的模式就是输入

另外一种写法是将模式符号和目标文件放在一块儿:

open LOG1,">/tmp/a.log"; 
open LOG2,">>/tmp/a.log";
open LOG3,"</tmp/a.log";

中间还能够有空格:

open LOG1,"> /tmp/a.log"; 
open LOG2,">> /tmp/a.log";
open LOG3,"< /tmp/a.log";

能够将目标文件赋值给一个变量,而后在open函数中使用变量名替换。

my $tmp_file = "/tmp/a.log";
open LOG1,">","$tmp_file";

若是要指明输入、输出的文件编码,则使用上面将"模式和路径分开"的方式。例如:

open LOG1,">:encoding(UTF-8)","/tmp/a.log";   # 以UTF-8编码方式写入数据
open LOG1,">>:encoding(UTF-8)","/tmp/a.log";
open LOG1,"<:encoding(UTF-8)","/tmp/a.log";   # 以UTF-8编码方式读入数据

须要注意,perl自身是没法打开外部文件的,它须要请求操做系统内核,让操做系统来打开文件。因此,打开文件正确、错误时,操做系统都会有相应的回馈信息。对于perl来讲,open函数的返回值就表示正确、错误打开文件。因此,经过如下方式能够判断是否正确打开:

my $success = open LOG,">","/tmp/a.log";
if(!success){
    exit 1;
}

更好、更经常使用的方式是使用die:

open LOG,">","/tmp/a.log"
    or die "open file wrong: $!";

或者使用autodie功能,当捕获到某些错误时,会自动调用die结束程序:

use autodie;
open LOG,">","/tmp/a.log";

没法打开文件的可能缘由有不少,好比读取时文件不存在,好比上级目录不存在,好比无权限等等。操做系统会向perl报告这些错误,使用"$!"能够引用操做系统向perl报告的错误,例如die "can't open file: $!";被触发时的消息以下:

can't open file: No such file or directory at myperl.plx line 5.

上面的"No such file or directory at myperl.plx line 5."就是$!收集和整理后的错误信息。

同理,关闭文件句柄错误也能够捕捉:

close DATA
    or die "Couldn't close file properly";

使用文件句柄:读取文件数据

例如,要从test.log文件中读取全部数据行,通常的流程以下:

#!/usr/bin/perl
use 5.010;

open LOG,"<","test.log"
    or die "open file wrong: $!"
while(<LOG>){
    chomp;
    say $_;
}

另外,从特殊文件句柄<STDIN><><ARGV>中读取数据时,因为它们是预约义好的,因此不须要先open。

使用文件句柄:写入数据到文件

要向文件中写入数据,可使用输出语句,如print/say和printf,要写多行的时候,可使用heredoc的方式。

在使用print/say/printf的时候,在这几个关键字后面接上文件句柄即表示本输出语句写入到此文件句柄中。其实,当它们不指定文件句柄的时候,所采用的就是默认的文件句柄STDOUT。

例如,以追加模式写入一行数据到test.log中。

#!/usr/bin/perl

use 5.010;

open LOG,">>","test.log"
    or die "Can't open file: $!";

say LOG "NEW LINE!";

再例如,向标准输出、标准错误中输出信息:

say STDOUT "NEW LINE!";
say STDERR "NEW LINE!";

选一个默认的输出文件句柄

注意是选择默认的输出文件句柄,不适用于输入的文件句柄。

默认状况下的默认输出文件句柄就是STDOUT,可是可使用select关键字本身选一个默认的输出文件句柄。只是须要注意的是,再将内容输出到自选的默认输出文件句柄结束后,应该从新选回STDOUT。

例如,读取某个文件的内容,追加剧定向输出到另外一个文件中:

#!/usr/bin/perl

open LOG,">>","test1.log" or die "Can't open file: $!";

select LOG;
while(<>){
    print "Line $. from $ARGV: $_";
}
select STDOUT;
print "restored default filehandler: STDOUT\n";

而后执行该perl程序(程序名:15.plx),并传递a.log做为命令行参数。做为运行结果,会将a.log中的数据追加到test1.log文件中,并输出一行内容到终端屏幕上。

$ perl 15.plx a.log
restored default filehandler: STDOUT

选择默认的文件句柄后,上面while循环中的print,等价于print LOG ...

一般,选择默认的文件句柄更经常使用于设置文件句柄是否要缓冲。例如,输出到下面三个文件句柄(LOG/STDERR/STDOUT)的数据不会缓冲,而是直接输出到文件句柄。

select    LOG; $| = 1;  # make unbuffered
select STDERR; $| = 1;  # make unbuffered
select STDOUT; $| = 1;  # make unbuffered

其中,控制输出的缓冲变量为$|,一般在使用管道、套接字的时候,可能不须要甚至不该该对数据进行缓冲,而是直接暴露给其它进程。

通常来讲,在超过一个文件句柄须要关闭缓冲时,不会使用这种select XXX; $|=1的方式,而是导入IO::Handle模块,而后使用它的autoflush(1)函数来实现关闭IO缓冲。

use IO::Handle;
FH1->autoflush(1);
FH2->autoflush(2);
FH3->autoflush(3);

autoflush(1)的功能等价于:

select( (select(NEWOUT), $| = 1 )[0] );

由于select()的返回值是当前标准输出的文件句柄,而后内层的select的结果和$| = 1的结果构成一个匿名列表,选择列表的第一个元素即以前标准输出的文件句柄,将其做为外层select的参数,即表示恢复了以前的文件句柄。(若是目前看不懂,请忽略)

关于IO Buffer

分为两种IO缓冲模式:block buffer、line buffer
block buffer是表示先积累必定数据量(好比一般是几K大小)以后再输出。line buffer是表示只有读取到了换行符的时候才输出。$|=1或者autoflush(1)都表示从block buffer(默认)切换成line buffer,而不是禁用(或关闭)buffer。
若是真要禁用buffer,可使用syswrite(),它会直接绕过buffer,也就是禁用buffer。

文件句柄变量

除了使用大写字母(通常状况下文件句柄都如此命名,称为裸句柄)的文件句柄,还可使用变量来命名文件句柄。

例如,使用变量表明的文件句柄:

my $rock_fh;
open $rock_fh,"<","/tmp/a.log"
    or die "Can't open file: $!";

或者:

open my $rock_fh,"<","/tmp/a.log"
    or die "Can't open file: $!";

使用变量文件句柄时:

while(<$rock_fh>){
    chomp;
    ...
}

不过有时候,使用文件句柄变量会产生歧义。例以下面的语句:

print $rock_fh;

perl并不知道$rock_fh是要输出的列表数据仍是输出的目标文件句柄。若是是目标文件句柄,这意味着print将$_写入到文件句柄$rock_fh中。若是是要输出的列表数据,因为$rock_fh是一个已定义好的文件句柄,print将输出它的引用(相似于:GLOB(oxABCDEF12))。

这时可使用大括号包围文件句柄变量。

print {$rock_fh};
print {$rock_fh} "hello world";

最后须要注意的是,裸句柄是包变量,在整个文件内都是有效的,而变量方式的文件句柄只在代码块范围内有效,出了本身的做用域范围就失效(自动关闭)

目录句柄

目录句柄和文件句柄相似,能够打开它,并读取其中的文件列表。只不过须要使用:

  • opendir替换open函数
  • 使用readdir来替换readline,readdir返回一个列表
    • readdir不会递归到子目录中
  • 使用closedir来替代close
  • 注意每一个Unix的目录下都包含两个特殊目录...
opendir JAVAHOME,"/usr/local/java"
    or die "Can't open dir handler: $!";
foreach $file (readdir JAVAHOME){
    print "Filename: $file \n";
}
closedir JAVAHOME;
相关文章
相关标签/搜索