Preface
进度两章,看来搞定这本书真的是需要话一些时间的。十天左右吧。怪我没弄实体书呗。
Content
我们已见过并用过一些内置的系统函数,像chomp,reverse和print等。
但是,就如同其他语言一样,Perl也可以让你创建子程序(subroutine),也就是用户自定义的函数。它让我们可以重复利用已有的代码。子程序的名称也属于Perl表示符的范畴(即由字母,数字和下划线组成,但不能以数字开头),有时候视情况会以“与号”(&)开头。关于何时可以省略以及何时必须加上这个放在表示符。
文件句柄
文件句柄(filehandle)就是程序里代表Perl进程(process)与外界之间的I/O联系的名称。也就是说,它是“这种联系”的名称,不是文件的名称。
给文件句柄起名就好比给Perl的其他标识符起名一样,必须以字母,数字以及下划线组成,但不能以数字开头。Larry建议你使用全大写字母来命名文件句柄,这样不仅开起来更加明显,也能避免与将要引入的(小写)保留字冲突,以免程序出错。
担忧6个特殊文件句柄名是Perl保留的,它们是:STADIN,STDOUT,STDERR,STDERR,DATA,ARGV以及ARGVOUT。
当程序启动时,文件句柄STDIN就是Perl进程和它的输入源之间的联系,也就是俗称的标准输入流(standard input stream)。它通常是用户的键盘输入,除非用户要求别的输入来源。
打开文件句柄
到目前位置,你已经看到过三种Perl提供的默认文件句柄————STDIN,STDOUT以及STDERR————它们都是由产生Perl进程的父进程(可能是shell)自动打开的文件或设备。
当你需要其他文件句柄时,请使用open操作符告诉Perl,要求操作系统为你的设备与外界架起一道桥梁。来看几个具体例子:
open CONFIG,'dino';
open CONFIG,'<dino';
open BEDROCK,'>fred';
open LOG,'>>logfile';
第一行会打开名为CONFIG的文件句柄,让它指向文件文件dino。换句话说,它会打开(已存在)。
使用encoding()的形式,还能指定其他类型的编码。我们可以通过下面这条单行命令打印出所有perl能理解和处理的字符编码清单:
perl -MEncode -le "print for Encode->encodings(':all')"
这个列表中出现的名字应该都可以拿来用字读取和写入文件时指定编码。但有些编码方式可能在其他机器上无法使用,关键还是要看相应。
这个列表中出现的名字应该都可以拿来用在读取和写入文件时指定编码。但有些编码方式可能在其他机器上无法使用,关键还是要看响应的编码系统是否安装在系统中。
除了转换字符编码之外,数据输入或输出过程当中还有其他层(layer)可以控制数据的转换操作。比如说,有时候你拿到的文件采用DOS风格的换行符,也就是文件中每行都以回车换行(carriage-return、linefeed,简写为CR-LF)对(也常写作“\r\n”)结尾。而Unix风格的换行符则只是一个“\n”。不管把谁当做谁,弄错了的话出来的结果就会很怪异。借助:crlf层,我们就可以自动解决这个问题。如果想要确保得到的文件每行都以CR-LF结尾,就得在操作该文件时使用这个特殊层:
open BEDROCK,’>:crlf’,
f
i
l
e
n
a
m
e
;
现
在
每
行
写
入
文
件
时
,
该
层
就
会
把
每
个
换
行
符
转
换
为
C
R
−
L
F
。
不
过
请
注
意
,
如
果
原
本
就
是
C
R
−
L
F
风
格
的
话
,
转
换
后
就
会
多
出
一
个
换
行
符
。
读
取
D
O
S
风
格
的
文
件
时
也
可
以
这
样
转
换
:
o
p
e
n
B
E
D
R
O
C
K
,
′
<
:
c
r
l
f
′
,
file_name; 现在每行写入文件时,该层就会把每个换行符转换为CR-LF。不过请注意,如果原本就是CR-LF风格的话,转换后就会多出一个换行符。 读取DOS风格的文件时也可以这样转换: open BEDROCK,'<:crlf',
filename;现在每行写入文件时,该层就会把每个换行符转换为CR−LF。不过请注意,如果原本就是CR−LF风格的话,转换后就会多出一个换行符。读取DOS风格的文件时也可以这样转换:openBEDROCK,′<:crlf′,file_name;
读入文件的同时,Perl会把所有CR-LF都转换为Unix风格的换行符。
以二进制方式读写文件句柄
在处理数据之前,其实不必预先知道它的实际编码方式,就算知道也不必每次都写明。在以前比较旧的Perl版本中,如果不希望转换换行符,比如某个二进制文件中恰好有一段字节顺序和换行符的内码相同,可以用binmode关闭换行符相关的处理:
binmode STDOUT;
binmode STDERR;
从Perl5.6开始,你可以在binmode的第二个参数的位置上指定层。如果你希望输出Unicode到STDOUT,就要确保STDOUT知道如何处理它拿到的数据:
binmode STDOUT,’:encoding(UTF-8)’;
如果不这么写的话,会得到警告信息(就算没有启用警告功能也会),因为STDOUT不知道该如何处理编码上的问题:
× wide character in print at test line 1.
实际上,不管输入还是输出,都可以用binmode指定特定行为。如果传到标准输入的是UTF-8编码的字符,那么应该事先告诉Perl按照UTF-8的方式处理:
binmode STDIN,’:encoding(UTF-8)’;
自动检测致命错误
从Perl 5.10开始为人称道的autodie变异指令已经成为标准库中的一部分。像下面这个例子,原理的写法是自己检查open的返回值并处理错误:
if(! open LOG, ‘>>’, ‘logfile’){
die 'Cannot create logfile: $!";
}
每次打开一个文件句柄都要这么写一遍的话,无疑是十分繁琐的。现在有了autodie编译指令,这部分工作便得以解放。如果open失败,它会自动启动die。
use autodie;
每个Unix(以及许多其他现代操作系统)上运行的程序都会有一个退出状态(exit status),用来通知操作系统该程序的运行是否成功。那些以调用其他程序为工作内容的程序(比如工具程序make)会查看那些程序的结束状态来判断一些顺利。所谓的“结束状态”其实只用一个字节来表示,所以它能传递的信息不多。传统上,零代表成功,非零代表失败。也许“1”代表命令参数中的语法错误,“2”代表处理某程序时发生了错误,“3”则可能代表找不到某个配置文件,各个程序的细节不尽相同。但是,“0”一定代表程序顺利完成。
改变默认的文件输出句柄
默认情况下,假如你不为print(或是printf,我们下面的说明对两者都有效)指定文件句柄,它的输出就会送到STDOUT不过,你可以使用select操作符来改变默认的文件句柄。
select BEDROCK;
print “I hope Mr. Slate doesn’t find out about this.\n”;
print “Wilma!\n”;
标量变量中的文件句柄
从Perl5.6开始,我们已经可以把文件句柄存放到标量变量中,而不必非得使用裸字。别小看这点差别,带来的好处可不少。成为标量变量后,文件句柄就可以作为子程序的参数传递,或者放在数组,哈希中排序,或者严格控制它的作用域。
Daily Task
终有一天,你会老去,而我,将会加冕为王!