Perl的IO操作(1):文件句柄
文件句柄
文件句柄用来对应要操作的文件系统中的文件,这么说不太严谨,但比较容易理解。首先为要打开的文件绑定文件句柄(称为打开文件句柄),然后在后续的操作中都通过文件句柄来操作对应的文件,最后关闭文件句柄。
如不理解文件句柄的概念,可将文件句柄看作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。
如果还不理解文件句柄,就把它想象成通道:perl程序中IO操作和磁盘上文件的通道。例如,print语句将数据通过某个通道输出到对应的文件中。
文件句柄和文件描述符
实际上,文件句柄和文件描述符是有区别的。文件描述符是一个数值,代表操作系统所使用的裸数据流,文件描述符是文件句柄的核心,文件句柄可以看作是文件描述符的更高一层次的封装,比如提供了和描述符有关数据流的输入、输出的buffer缓冲。也就是说,文件句柄比文件描述符多一些额外的功能。
我们可以为任意要操作的文件定义一个文件句柄。通常,使用大写字母作为文件句柄的名称。
例如,下面打开一个文件句柄LOG,这个文件句柄对应的文件是/tmp/a.log,操作模式是追加写入(和shell中的追加重定向是相同的意思)。然后向LOG文件句柄中输入一段数据,最后关闭文件句柄。
open LOG,">>/tmp/a.log";
print LOG "haha, hello world";
close LOG;
一般来说,打开了文件句柄后,在操作完成后要关闭文件句柄以便节省操作系统"打开文件数量限制"的资源。但perl有时候比较智能,会在某些时候自动帮我们关掉文件句柄。而且,当打开一个文件句柄后,再次去打开这个文件句柄时,perl会先关闭这个文件句柄再打开这个文件句柄,这称为"文件句柄的reopen",它只是隐式地关闭并重新打开,perl并不认为中间涉及了关闭操作(例如reopen时行号不会重置)。
打开文件句柄
要打开文件句柄,使用open函数。open函数的功能其实很丰富,如有需要,可去官方手册查看:http://perldoc.perl.org/functions/open.html
打开(open)文件是有目的的:为了读取?为了写入?为了追加写入?这是操作模式。在open文件时,需要指明操作模式。此外,还要给定要关联的文件路径,路径可以是绝对路径,也可以是相对当前perl程序的相对路径。
另外注意,文件句柄需唯一,不能出现重名。
例如:
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;
Perl的IO操作(1):文件句柄的更多相关文章
- Perl的IO操作(2):更多文件句柄模式
open函数除了> >> <这三种最基本的文件句柄模式,还支持更丰富的操作模式,例如管道.其实bash shell支持的重定向模式,perl都支持,即使是2>&1 ...
- python之协程与IO操作
协程 协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B ...
- Linux系统编程--文件IO操作
Linux思想即,Linux系统下一切皆文件. 一.对文件操作的几个函数 1.打开文件open函数 int open(const char *path, int oflags); int open(c ...
- Pandas系列(十一)-文件IO操作
数据分析过程中经常需要进行读写操作,Pandas实现了很多 IO 操作的API,这里简单做了一个列举. 格式类型 数据描述 Reader Writer text CSV read_ csv to_cs ...
- python中的IO操作
python中的基本IO操作: 1) 键盘输入函数:raw_input(string),不作处理的显示,与返回. input(string),可以接受一个python表达式作为返回,python内部得 ...
- lua io操作(一)
最近在做可视化窗口数据配置 使用的lua 语言,免不了IO操作 通过查阅资料,做了如下总结,易于理解 lua里的文件读写模型来自C语言,分为完整模型(和C一样).简单模型. 1.简单模型 io.inp ...
- 9.2 Go 文件IO操作
9.2 Go 文件IO操作 1.1.1. bufio包 带有缓冲区的IO读写操作,用于读写文件,以及读取键盘输入 func main() { //NewReader返回一个结构体对象指针 reader ...
- [.NET] 利用 async & await 进行异步 IO 操作
利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html 序 上次,博主 ...
- 文件IO操作..修改文件的只读属性
文件的IO操作..很多同行的IO工具类都是直接写..但是如果文件有只读属性的话..则会写入失败..所以附加了一个只读的判断和修改.. 代码如下: /// <summary> /// 创建文 ...
随机推荐
- LoRa与NB-IoT对比(转载)
物联网的基本架构包括三个层面:感知层.网络层和应用层. 物联网架构图 感知层通过传感器采集某些数据(声.光.电等),基于网络层的终端模组,对接到网络层的基站,实现数据采集后的传输. 网络层负责将感知层 ...
- Think twice before starting the adventure
杂文一篇. 1. 取名字真心是一件特别困难的事情.这位独立开发者花了将近两天的时间,给他的私人项目取了个名字:这篇博客<为何我不鸟你的开源项目>里显然还忽视了一个原因,就是名字取得太烂以至 ...
- OpenGIS
OpenGIS(Open Geodata Interoperation Specification,OGIS-开放的地理数据互操作规范)由美国OGC(OpenGIS协会,Open Geospatial ...
- 监听HTTP请求
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Oracle.DataAccess.Client; using System; usi ...
- 记一次生产环境thrift服务的配置问题
问题现象 有客户反馈我们的产品有时反应很慢,处理会出现超时. 问题分析过程 1.第一反应可能是用户增加,并发量太大了,询问了运营,最近用户注册数据并没有猛增. 2.分析access日志,发现有隔一段时 ...
- Firewalld的结构
原文地址:http://www.excelib.com/article/287/show firewalld简介 Centos7中默认将原来的防火墙iptables升级为了firewalld,fire ...
- Java基础巩固——异常
基础回顾 什么是异常? 在Java程序运行时,常常会出现一些非正常的现象,这种情况称为运行错误.根据其性质可以分为错误和异常. Java程序中所有抛出的异常都必须从Throwable派生而来.类Thr ...
- 【vim】模式与模式切换
很多初学者启动vim后,不知道怎么输入字符:按了半天字母,结果屏幕还是空的. vim和记事本或WORD不一样,不是一打开后就可以输入文字,此时它处于正常模式. vim一共有4个模式: 正常模式 (No ...
- 增删改查Spring+MyBatis
其实这次写这个增删改查,我的收获很大,在同学的帮助下和老师的推动下,我也是学会了很多的技能点. 1.显示数据 显示数据对我而言可以说很好做,因为我以前增删改查做了有N遍,但是我却每次都是无功而返,半途 ...
- hdf 5文件格式及python中利用h5py模块读写h5文件
h5文件格式,HDF 的版本 5(HDF 版本 5不与 HDF 版本 4 及早期版本兼容).HDF是什么呢?就是Hierarchical Data Format,可以存储不同类型的图像和数码数据的文件 ...