Perl-文件测试2

1. 同一文件的多项属性测试

如果要一次测试某个文件的若干属性,可以将各个文件测试组成一个逻辑表达式

if (-r $filename and -w $filename) {
	...
}

这是个很耗费资源的操作,每次进行文件测试时,perl都得从文件系统中取出所有相关信息(实际上,每次perl都在内部做了一次stat操作)。
虽然在做-r测试的时候,我们已经拿到了所有相关信息,可到了做-w测试的时候,perl又要去取一遍相同的信息

perl有个特别的简写可以避免这些重复劳动,它就是虚拟句柄_,它会告诉perl用上次查询过的文件信息来做测试,现在perl只需要查询一次文件信息即可

if(-r $filename and -w _){
	...
}

不是只能在一条语句中连续使用_

if(-r $filename){
	print "The file is readable!\n";
}

if(-w _){
	print "The file is writable!\n";
}

这么用的时候,必须要清楚代码最后一次查询的是否为同一个文件。如果在两个文件测试之间又调用了某个子程序,那么最后一个查询的文件可能会变化

if(-r $filename){
	print "The file is readable!\n";
}

lookup($other_filename);

if(-w _){
	print "The file is writable!\n";
}

sub lookup{
	return -w $_[0];
}

2. 栈式文件测试操作符

在perl5.10之前,如果要一次测试多个文件属性,只能分开为若干独立的操作,就算是使用了虚拟句柄_也是这样
比如说,我们想测试某个文件是否可读写,就必须分别做可读测试和可写测试

if(-r $filename and -w _){
	print "The file is both readable and writable!\n";
}

如果能一次完成两个测试就好了!从perl5.10开始,我们可以使用“栈式(stack)”写法将文件测试操作符排成一列,放在要测试的文件名前

use v5.10;

if(-w -r $filename){
	print "The file is both readable and writable!\n";
}

在使用栈式写法是,靠近文件名的测试会先执行,次序为从右往左,不过一般来说,测试次序不是很重要

在一些复杂情况下,这种栈式文件测试特别好用

use v5.10;

if(-r -w -x -o -d $filename){
	print "My directory is readable, writable, and executable!\n";
}

如果你需要的是返回非布尔值的测试,栈式写法很垃圾

use v5.10;

if(-s -d $filename < 512){ # 本来想确认某个小于512字节的目录。这样是错误的!不要这样做!
	say 'The directory is less than 512 bytes!';
}

当-d返回假时,perl将假值同数字512作比较。比较的结果就变成真,因为假等效为数字0,而0永远小于512

if(-d $filename and -s _ < 512){
	print "The directory is less than 512 bytes!\n";
}

3. stat和lstat函数

之前说的文件测试符可以获取某个文件或文件句柄的各种常用属性,不过还有很多属性信息没有对应的文件测试操作符,如果想知道文件所有其它相关信息,可以用stat函数
stat函数的参数可以是文件句柄(包括虚拟句柄_),或是某个会返回文件名的表达式。如果stat函数执行失败(常常是因为无效的文件名或者文件不存在),它会返回空列表,要不然返回一个含13个数字元素的列表

my($dev, $ino, $mode, $nlink, $uid, $gid, $rdev,
	$size, $atime, $mtime, $ctime, $blksize, $blocks)
		= stat($filename);

$dev$ino
即文件所在设备的编号与文件的inode编号,这2个编号决定了文件的唯一性。即使文件具有多个不同的文件名(使用硬链接创建),设备编号和inode编号的组合依然是独一无二的

$mode
文件的权限位集合还包含其它信息位

$nlink
文件或目录的(硬)链接数,也就是这个条目有多少个真实名称。这个数值对目录来说总会是2或更大的数字,对文件来说则(通常)是1

$uid$gid
表示文件拥有者的用户ID及组ID的数字

$size
以字节为单位的文件大小

$atime$mtime$ctime
3种表示从纪元(epoch)算起的秒数的时间戳。在unix与某些其它系统中,纪元从公元1970年世界标准时间的午夜算起

对符号链接名调用stat函数,会返回符号链接指向对象的信息,而非符号链接本身的信息(出发该链接指向的对象目前无法访问)。如果需要的是符号链接本身的信息(一般没用),可以用lstat函数

模块File::stat提供了兼容stat且更为友好的操作界面

4. localtime函数

获得的时间戳值(比如从stat函数返回的时间戳)看起来通常像12535752655这样的形式,这个数字实在是不方便,除非想通过剑法来比较两个时间戳大小。
perl可以在标量上下文中使用localtime函数将时间戳值转换成比较容易阅读的形式

my $timestamp = 1454133253;
my $date = localtime $timestamp;

在列表上下文中,localtime函数会返回一个数字元素组成的列表

my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime $timestamp;

$mon: 范围从0到11的月份值,可以用来索引月份名
$year: 从1900年开始算的年份,这个值+1900就是实际的年份
$wday: 从0(星期天)到6(星期六)
$yday: 表示目前是今年的第几天,从0(1月1日)到364/365(12月31日)

gmtime函数和localtime函数一样,不过gmtime函数返回的是世界标准时间(格林威治时间)
可以用time函数来从系统时钟取得当前的时间戳

不提供参数的情况下,不论localtime或gmtime函数,默认情况下都使用当前time返回的时间值

my $now = gmtime; # 取得当前世界标准时间的时间戳字符串

5. 位运算操作符

如果需要逐位进行运算,比如对stat函数返回的权限位进行处理,就必须用到位运算操作符
位运算操作符对数据执行二进制数学运算

表达式意义
10 & 12按位与,哪些位在两边同时为真(8)
1012
10 ^ 12按位异或,哪些位在任何一边为真,但另一边为假(6)
6 << 2按位左移,将左边操作数向左移动数位,移动位数由右边操作数指定,并以0来填补最低位(24)
25 >> 2按位右移,将左边操作数向右移动数位,移动位数由右边操作数指定,并丢弃移出的最低位(6)
~ 10按位取反,也称为取反码,返回操作数逐位反向后的数值(0xFFFFFFF5)

位操作的结果可以给chmod使用

# $mode是从配置文件CONFIG的stat信息中取出的状态值
warn "Hey, the configuration file is world-writable!\n"
	if $mode & 0002; # 配置文件有安全隐患
my $classical_mode = 0777 & $mode; # 遮蔽额外的高位
my $u_plus_x = $classical_mode | 0100; # 将一个位设为1
my $go_minus_r = $classical_mode & (~ 0044); # 将两个位都设为0

6. 使用位字符串

所有这些位运算操作符既可以操作位字符串(bitstring),也可以对整数进行操作。如果操作数都是整数,结果也会是整数(整数至少会是一个32位整数,但如果你的硬件支持更多位的话,就会更大。例如在64位的机器上,~10的结果会是0xFFFFFFFFFFFFFF5,而不是32机器上的0xFFFFFFF5)

如果位运算操作符的2个操作数都是字符串的话,perl会把它当成位字符串来处理。也就是说"\xAA"|"\x55"的结果会是"\xFF"。需要注意的是,这个例子里的值都是单字节的字符串,其结果是8个比特位上都是1的字节。perl对位字符串的长度没有限制

这是少数perl区分字符串和数字的地方

只要perl判断其中一个操作数是数字类型,那就会按照数字的位运算方式执行

use v5.10;

my $number = 137; 
my $number_str = '137';
my $string = 'Amelia';

say "number_str & string: ", $number_str & $string; # 按照字符串方式执行的位运算
say "number & string: ", $number & $string; # 其中一个操作数是数字,perl把另一个操作数先转换成数字,结果是0
say "number & numbers_string: ", $number & $number_str; # 将'137'转换为数字137,和另一个操作数完全相同,因为每个比特位上的内容都相同,所以最后的运算结果还是原来的数字137
say "number_str & string: ", $number_str & $string; # 这个结果居然是0!!!

当我们重新运行第一行语句时,结果却发生了变化!虽然我们没有手动修改变量值,但在前2条语句perl相继修改了$number_str$string的值为数字类型。在perl做这件事的背后,其实默默保存了转换结果以备重复实验。当执行最后一行时,perl查看这2个变量是否已有转好的结果,一看都是数字,于是按照两边都是数字的方式执行位运算

其实perl有一个双值变量(dualvar)的概念。标量可以同时有2个版本的值,一个数字类型的,一个字符串类型的。大部分时候这没啥问题,某些情况下反而更方便。比如系统错误变量$!的字符串版本时错误信息描述,数字版本是错误代号。可以参考Scalar::Util模块的说明

自perl5.22开始增加了实验性的新特性以解决类似的奇怪问题。在使用操作符进行计算时,我们最好能明确操作数以何种方式参与计算,不管操作数之前曾有过什么历史。如果要执行数字位运算启用bitwise特性会令位操作符将操作数统一按照数字类型计算

use v5.22.0;
use feature qw(bitwise);
no warnings qw(experimental::bitwise);

my $number = 137;
my $number_str = '137';
my $string = 'Amelia';

say "number_str & string: ", $number_str & $string;
say "number & string: ", $number & $string;
say "number & number_str: ", $number & $number_str;
say "number_str & string: ", $number_str & $string;

这一次输出的第一行不再是天书了,即使2边的操作数都是字符串,但perl将它们都当数字,最后计算结果是0

number_str & string: 0
number & string: 0
number & number_str: 137
number_str & string: 0

如果要以字符串方式进行位运算,bitwise特性增加了一个新的位操作符写法,后补一个.表示

use v5.22.0;
use feature qw(bitwise);
no warnings qw(experimental::bitwise);

my $number = 137;
my $number_str = '137';
my $string = 'Amelia';

say "number_str &. string: ", $number_str &. $string;
say "number &. string: ", $number &. $string;
say "number &. number_str: ", $number &. $number_str;
say "number_str &. string: ", $number_str &. $string;
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值