Perl使用一种叫“文件句柄”的变量来操作文件
在文章末尾将补充讲解Perl5新加入的语法特性
文件句柄(filehandle)是文件操作中用来存放文件唯一标识符的名称空间
或者说,文件句柄是一个I/O连接的名称
在文件I/O中,要从一个文件读取数据,应用程序要调用操作系统函数并传入文件名,然后选取一个到该文件的路径来打开文件;用来沟通文件和I/O原始数据的就是文件句柄
Perl内置六种保留文件句柄 STDIN
STDOUT
STDERR
DATA
ARGV
ARGVOUT
对应,标准输入、标准输出、标准错误、从执行它的脚本获取输入、命令行参数、命令行参数输出
文件句柄是文件操作的基础,可以作为参数传给「打开文件函数」
文件句柄通常大写
open CONFIG, '读file.txt';
其中,“CONFIG”就是程序员指定的文件句柄,该句打开文件句柄OCNFIG,并从file.txt中读入数据(默认情况下就是“读”,所以 ‘<’符号是可省略的)
几种open函数的形式:
open HANDLE1, '; #读入数据
open HANDLE2, '>b.txt'; #写入数据
open HANDLE3, '>>c.txt'; #追加数据
所谓“关闭文件句柄”,就是改告诉操作系统“程序对文件的数据流操作已经完成,请系统将尚未写入的输出数据写入磁盘以免有人等着使用”
假设你熟悉I/O系统,应该知道这里还有一些细节:一般来说,当文件句柄关闭时,如果文件中仍有输入它会被忽略;如果管道中仍有输入,那么对这个管道进行读写操作的程序会收到管道被关闭的信号;如果有文件或管道的输出,那么缓冲区会被刷新(也就是保存在缓冲区的内容会马上发送出去);如果文件句柄加了锁,那么该锁会被释放……
当重新打开文件句柄时,Perl会自动关闭原先的文件句柄。在程序结束时Perl也会自动关闭所有的文件句柄
正因为Perl是这样贴心,以至于我们根本不需要操作文件句柄的关闭问题;但是如果要代码写的好看些,最好在适当的时候加上close
函数
Perl使用die
函数来捕获异常
可以通过操作的状态返回码来避免异常:
my $success = open LOG, '>>', 'logfile';
if(! $success){
#open操作失败
...
}
现在可以使用die函数来捕获异常(规范写法):
if(! open LOG, '>>', 'logfile'){
die "Cannot create logfile: $!";
}
如果open失败,die会终止程序的运行,并且告诉我们无法创建日志文件
冒号后面的$!
代表(保存着)“可读的系统错误信息”,如permission denied
、file not found
之类的语句
另外die函数还会打印出出现问题的程序所在行数
Cannot create logfile: permission denied at your_program line 1234
如果不想输出行数信息,可以在die函数的末尾加上\n
换行符
if(@ARGV<2){
die "Not enough arguments\n";
}
warm
函数的功能和die
函数的功能差不多
不同的是,warm
函数不会终止程序的运行
让我们回到文件操作的话题
if(! open PASSWD, "/etc/passwd"){
die "How did you get logged in? ($!)";
}
while(){
chomp;
...
}
在这个栗子中可以看到,所谓的行输入操作符<>
不过是括住输出信息(文件句柄)的括号而已
#函数入操作符的使用:
“行输入操作符”也叫“钻石操作符”(长得像~)
钻石操作符用来从终端命令行读取参数,并解析文件句柄用于文件操作
print LOG "Hello world\n"; #输出到文件句柄LOG
print STDERR "World hello\n";
默认情况下,文件输出句柄是STDOUT
Perl使用select
来改变默认的文件输出句柄
select
函数的作用域,除非显式重新改回原来STDOUT
否则将一直延续到程序结束
select MYHANDLE;
print "What are you doing?\n"
print "I am typing with Perl...\n";
现在默认情况下这两句话都被输出到文件句柄MYHANDLE中去了..
$|
Perl中的$|
变量如果被设置为1,那么之后所有的输出操作将无视缓冲区的存在而“立即刷新缓冲区”,那么输出的内容会被立即发送(如果发送到显示屏就是立即显示)
select LOG;
#|=1; #不要将LOG的内容保存在缓冲区
select STDOUT;
#...
print LOG "This is something...\n";
以上,比如读取监视某个耗时程序的实时日志
1. open函数有三个参数,实现了参数分离,更安全
open CONFIG, '<', 'dino';
open BEDROCK, '>', '$filename';
2. 三个参数允许指定数据的编码方式
open CONFIG, '<:encoding(UTF-8)', 'dino';
#:encoding(utf-8)简写:utf-8但不推荐简写
3. 自动检测致命错误
use autodie;
4. 使用say来输出
use 5.010;
say "Hello!"; #打印变量并自动在末尾添加一个换行符
5. 标量中的文件句柄
从Perl5.6开始,我们可以把文件句柄存放在标量中,而不必非得使用“裸字(字面量)”
在称为标量之后,文件句柄就可以作为函数参数传递给子程序
my $rocks_fh #使用词法变量my确保该变量之前是空的
open $rocks_fh, '<', 'rocks.txt'
or die "Could not open rocks.txt: $!";
或者
open my $rocks_fh, '<', 'rocks.txt'
or die "Could not open rocks.txt: $!";
在得到保存文件句柄的变量后,直接把原先使用裸字的地方就可以了
while(<$rocks_fh>){
chomp;
...
}
输出信息到文件句柄也可以使用到这个变量,在原先使用裸字的地方使用这个标量便能以适当的方式打开该文件
open my $rocks_fh, '>>', 'rocks.txt'
or die "Could not open rocks.txt: $!";
foreach my $rock(qw(slate lava granite)){
say $rocks_fh $rock
}
print $rocks_fh "limestone\n";
print $rocks_fh;
注意,Perl能够自动判断,如果print
语句的第一个参数之后没有逗号,就说明它是一个文件句柄(不!要!加!逗!号!)
print $rocks_fh, "limestone\n"; #错误!!(无法判断文件句柄)
一般短小的程序,比如系统管理员的工具脚本,用裸字也没什么不好;不过对于大一点的项目或应用程序来说,使用标量可以精确控制文件句柄的作用域,方便调试和维护