文件句柄

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

如不理解文件句柄的概念,可将文件句柄看作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):文件句柄的更多相关文章

  1. Perl的IO操作(2):更多文件句柄模式

    open函数除了> >> <这三种最基本的文件句柄模式,还支持更丰富的操作模式,例如管道.其实bash shell支持的重定向模式,perl都支持,即使是2>&1 ...

  2. python之协程与IO操作

    协程 协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B ...

  3. Linux系统编程--文件IO操作

    Linux思想即,Linux系统下一切皆文件. 一.对文件操作的几个函数 1.打开文件open函数 int open(const char *path, int oflags); int open(c ...

  4. Pandas系列(十一)-文件IO操作

    数据分析过程中经常需要进行读写操作,Pandas实现了很多 IO 操作的API,这里简单做了一个列举. 格式类型 数据描述 Reader Writer text CSV read_ csv to_cs ...

  5. python中的IO操作

    python中的基本IO操作: 1) 键盘输入函数:raw_input(string),不作处理的显示,与返回. input(string),可以接受一个python表达式作为返回,python内部得 ...

  6. lua io操作(一)

    最近在做可视化窗口数据配置 使用的lua 语言,免不了IO操作 通过查阅资料,做了如下总结,易于理解 lua里的文件读写模型来自C语言,分为完整模型(和C一样).简单模型. 1.简单模型 io.inp ...

  7. 9.2 Go 文件IO操作

    9.2 Go 文件IO操作 1.1.1. bufio包 带有缓冲区的IO读写操作,用于读写文件,以及读取键盘输入 func main() { //NewReader返回一个结构体对象指针 reader ...

  8. [.NET] 利用 async & await 进行异步 IO 操作

    利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html  序 上次,博主 ...

  9. 文件IO操作..修改文件的只读属性

    文件的IO操作..很多同行的IO工具类都是直接写..但是如果文件有只读属性的话..则会写入失败..所以附加了一个只读的判断和修改.. 代码如下: /// <summary> /// 创建文 ...

随机推荐

  1. servlet从mysql中取数据时出现的汉字编码问题

    取出的汉字都是问号 之后根据网友提示在输出之前对response进行编码设置 正常显示

  2. 《vue.js快跑》总结:为什么选择VUE

    2019-3-31 为什么选择Vue 有这个一个需求,我们需要根据后端数据接口请求返回的数组在页面中按列表展示? 传统上我们使用jQuery的Ajax发送http请求,获取数据.判断列表数据是否存在, ...

  3. android-基础编程-Notification

    Notification 的创建主要涉及到 Notification.Builder . Notification . NotificationManager . Notification.Buile ...

  4. python图形界面编程

    EasyGui(easygui-docs-0.96\tutorial\index.html) import easygui as g import sys while 1: g.msgbox('mes ...

  5. LeetCode刷题:第四题 寻找两个有序数组的中位数

    题目描述: 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n)). 你可以假设 nums1 和  ...

  6. 自定义Token的CAS登录

    工作中实际遇到的需求,我们有一个旧系统,用了CAS的单点登录,现在有一个外部系统,准备从它那里单点进来,这个外部系统提供了一个token参数来标记这是哪一个用户,我们用他们提供的方式解析出对应的用户, ...

  7. unity API 之EventSystem.current.IsPointerOverGameObject()

    命名空间 :UnityEngine.EventSystems 官方描述: public bool IsPointerOverGameObject(); public bool IsPointerOve ...

  8. C#介绍RabbitMQ使用篇一HelloWorld

    RabbitMQ官网官方介绍: 译文: RabbitMQ是目前部署最广泛的开源消息代理(何为代理?可以理解为一个提供功能服务的中间件). 在全球范围内的大小企业中的生产环境中,RabbitMQ的部署两 ...

  9. Python教程:从零到大师

     首先, 什么是Python? 用python作者Guido van Rossum自己的话来说,Python是这样的一门语言:   "它是一门高级编程语言, 它的核心设计理念是让所有代码变得 ...

  10. WebView的知识

    一.基本介绍 WebView是一个显示页面的组件. 二.基本使用 访问网页需要网络,所以在AndroidManifest.xml文件中添加网络权限. <uses-permission andro ...