Perl基础知识(第二章)
一、分子
=......=cut,可用于行注释,忽略的文本被认为是pod
二、内置的数据类型
perl的三种基本数据类型是:标量,标量数组和标量散列(hash).
1、peri认为负数脚标也是合法的:负数脚标是从后向前记数你的数组。
2、除变量外,还有一些其它perl抽象你也可以认为是数据类型,比如文件句柄,目录句柄,格式串,子过程(子函数),符号表和符号表入口等。
3、$x=$y,$x称为lvalue(左值),$y称为rvalue(右值),还有第三种数值,叫临时值。
三、变量
趣味字符(funny character):
${days}---和$days一样,不过在字母数字前面不易混淆。
$Dog::days---在Dog包里面的不同的$days变量
$#days---数组@days的最后一个索引。
$days->[28]---$days一个引用,指向数组的第二十九个元素。
$days[0][2]---多维数组
$days{200}{"Feb"}---多维散列
$days{2000,:"Feb"}---多维散列枚举
@days[3,4,5]---包含($days[3],$days[4],$days[5])
@days[3..5]同上
@days{'jan','Feb'}---包含($days{'Jan'},$days{'Feb'})片段的散列
@days =1..7;
四、名字
1、名字空间:存储变量本身的空间,两种类型:符号表和词法范围。
符号表:是全局的散列,用于存储全局变量进入符号表的入口。
词法范围:是未命名的中间结果暂存器。附在程序的一块代码后面,包含只能被该块所见的变量。
子过程可以用一个&开头命名,调用子过程的时候这个趣味字符是可选的。
类型团(typeglobs):用一个带前缀*命名的符号表入口。
推荐使用带大写字符的名字做标签和文件句柄,因为绝大多数保留字都是完全小写。
用户定义的模块通常都是首字母大写的名字命名的,这样就不会和内建的模块冲突,因为内建模块都是以小写字母命名的。
一个名字开头不是字母,数字或下划线,这样的名字限于一个字符(比如$?或$$),通常有预定的意义。
趣味后面的名字一定是标识符。名字和标识符有区别,名字一般指其全称,表明自己位于哪个符号表的名字。$santa::Hwlper::rudolph::nose,这里符号表的名字是santa::Hwlper::rudolph::,标识符就是$nose.
符号表也被称为包(package),因此这些变量常被称为包变量。
附着在词法范围上的变量不属于任何包,因此词法范围变量名字可能不包含::序列。(词法范围变量都是用my定义式定义的)
2、 名字查找:
1、perl预先在最先结束的块里面查找,看看该变量是否有用my(或our)定义在该代码块里。
2、上面过程失败,peril尝试在包围该代码段的块里查找,仍然是在更大的代码块里查找词法范围的变量。如果没发现定义,重复第二步直到用光所有上层 闭合块。
3、开始检查整个编辑单元,把它当作代码块寻找声明。
4、在任何词法范围内都没有包声明,perl就在未命名的顶层包里查找,这个包就是main.
五、标量值
1、数字文本
$x=6.02e23 #科学记数
$x=4_294_967_296#提高可读性的下划线
$x=03723#八进制
$x=0xffff#十六进制
$x=0b1100_0110#二进制
下划线只能用于你程序里面声明的数字文本,而不能用于从其他地方读取的用做数字的字串。
字串到数字的自动转换并不识别这些前缀,必须用oct函数做显示转换。
2、字串文本
字串文本通常被单引号或者双引号包围。
双引号包围的字串文本会做反斜杠和变量替换,而单引号包围的字串文本不会。
在双引号中做的变量代换只能代换以$或@开头的表达式。但假如@后面跟着一个字母数字字符,而它又不是数组或片段的标识符,就必须用反斜杠逃逸(\@)。
$price='$100';#单引号中不替换。
print"the price is $price";#将$price替换成$100
输出结果:the price is $100
可以再标识符周围放花括弧,使之与后面的字母数字区分开来:"How ${verb}able"
$days{'Feb'}可以写作:$days{Feb}
$days{'February 29th'}不能写作:$days{February 29th}.
@days{'Jan','Feb'}不能写做:@days{Jan,Feb}
3、"此处文档"
起始分隔符是当前行,结束分隔符是一个包含你生命的字串的行,你所声明的用以结束引起材料的字串跟在一个<<后面。所有当前行到结束行(不包括)只见的行都是字串的内容。结束字串可以是一个标识符或者某些引起的文本。
在<<和未引起的标识符之间不能有空白(用带引号的字串做标识符,则可以有空白)
结束字串必须在终止行独立出现。
print <<EOF;
the price is $price.
EOF
4、V-字串文本
一个以v开头,后面跟着一个或多个用句点分隔的整数的文本,会被当作一个字串文本。
$crif=v13.10;#ASCII码回车,换行
六、 环境
1、标量和列表环境
在perl脚本里激活的每个操作都是在特定的环境里进行的。
给一个标量变量,一个数组或散列的标量元素赋值,在右手边就会以标量环境计算:
$x=funkshun();
$x[1]=funkshun();
$x{"ray"}=funkshun();
给一个数组或者散列,或者他们的片段赋值,右手边就会以列表环境进行计算
@x=funkshun();
@x[1]=funkshun();
@x{"ray"}=funkshun();
%x=funkshun();
即使你用my或our修改项的定义,这些规则也不会改变:
my $x=funkshun();#scalar context
my @x=funkshun();#list context
my($x)=funkshun();#list context
2、布尔环境:一个特殊的无所谓标量环境
一个标量不是空字串""或者数字0(或者它的等效字串,"0")那么就是真。一个引用总是真,因为它代表一个地址,而地址从不可能是0.一个未定义值总是假。(列表值没有布尔值,因为列表值从来不会产生标量环境)
七、列表值和数组
@stuff=("one","two","three");
$stuff=("one","two","three");#结果为three
@stuff=("one","two","three"); $stuff= @stuff;#结果为3,返回的是数组长度
(@stuff,@nonsense,funkshun())
@froots=qw(apple banana carambola coconut guava);
(List)[List] #$hexdigit= ("one","two","three") [1]
1、列表赋值
你可以给一个列表里的undef赋值,这一招可以很有效的将一个函数的某些返回值抛弃:
($dev,$ino,undef,undef,$uid,$gid)=stat($file);
一个列表元素可以是一个数组或散列:
($a,$b,@rest)=split;
my($a,$b,%rest)=@arg_list;
可以给空列表赋值:()=funkshun();这样会导致在列表环境里调用函数,但丢弃返回值。假如没有赋值的情况下调用此函数,就会在空环境中调用,空环境是标量环境,行为可能会不同。
在标量环境里的列表赋值返回赋值表达式右边生成的元素的个数:
$x=(($a,$b)=(7,7,7));#把$x设为3,不是2
$x=(($a,$b)=funk());#把$x设为funk()的返回数
$x=(()=funk());#把$x设为funk()的返回数
2、数组长度
可以通过在标量环境里计算数组@days而获取数组@days里面的元素个数,比如:
@days+0;#强制将@days处于标量环境中
$day=@days; #强制将@days处于标量环境中
这只对数组有效,对列表就无效,因为一个逗号分隔的列表在标量环境里返回最后一个值。
$#days返回数组里最后一个元素的角标,或者说长度减一。给$#days赋值则修改数组长度。
@whatever=0; $#whatever=-1;这两个语句等效
scalar(@whatever)==$#whatever+1;这个表达式总是为真。
@whatever=();截断一个数组并不回收其内存,必须undef(@whatever)来把它的内存释放回你的进程的内存池里。
3、散列
给一个散列赋一个普通列表的值,列表里的每一对值将被当做一对键字/数值关联:
%map=('red',0xff0000,'green',0x00ff00,'blue',0x0000ff);
%map=();#先清除散列
$map{red}=0xfff0000;#和上面的是一样的
=>操作符有更好的可读性, %map=('red'=>0xff0000,'green'=>0x00ff00,'blue'=>0x0000ff);
可以在一个列表环境里使用散列变量(%hash),这种情况下它把它的键字/数值兑换成列表,但并不意味着以某种顺序初始化的散列就应该同样的顺序恢复出来。散列在系统内部实现上是使用散列表来达到高速查找,记录存储的顺序和内部用于计算记录在散列表里的位置的散列函数有关,所以恢复出来的时候看起来是随机的顺序。
在标量环境里计算散列变量的数值时,返回的值是一个用斜线分隔的已用空间和分配的总空间的值组成的字串。
要算出一个散列里面的键字的数量,在标量环境里使用keys函数,scalar(keys(%HASH))。
不要将多维散列仿真和片段混淆,前者表示一个标量数值,后者则是一个列表数值。
$hash{$x,$y,$z};#单个数值
@hash{$x,$y,$z};#一个三个值的片段
4、型团(typeglob)和文件句柄
类型团用以保留整个符号表记录(符号表记录*foo包括$foo,@foo,%foo,&foo),类型团的类型前缀上一个*,因为它代表所以类型。
类型团的一个用途是用于传递或者存储文件句柄。
$fh=*STDOUT;
或者作为一个真的引用,像这样:
$fh=\*STDOUT;
类型团如今的主要用途是把一个符号表取另一个符号表名字做别名。
*foo=*bar;所以叫foo的东西都是每个对应的叫bar的同意词,可可以引用实现:
*foo=\$bar;这样$foo就是$bar的一个别名,而没有把@foo做成@bar的别名,或者%foo做成%bar的别名。
八、输入操作符
1、命令输入(反勾号)操作符
$info=`finger $user`;用反勾号引起的字串首先进行变量替换,得到的结果然后被系统当做一个命令行,而且那个命令的输出成为伪文本的值。标量环境中,返回一个包含所有输出的字串。
反勾号的一般形式是qx//,这个操作符的作用完全和普通的反勾号一样。如果你选择了单引号做你的分隔符,那命令行就不会进行双引号替换了。
$perl_info=qx(ps $$);#这里$$是perl的处理对象
$perl_info=qx'ps $$';#这里$$是shell的处理对象
2、行输入(尖角)操作符
当且仅当输入操作符是一个while循环的唯一一个条件的时候,其值自动赋予特殊变量$_。
while(defined($_=<STDIN>)){print $_;}#最长的方法
while($_=<STDIN> ){print;}#
while(<STDIN>{print;})#短形式
for(;<STDIN>;){print;}
print $_ while defined($_=<STDIN>);#长的语句修改
print while $_=<STDIN>;#明确$_
print while<STDIN>;#短的语句修改
这样的特殊技巧要求一个while循环。如果你在其他的什么地方使用这样的输入操作符,你必须明确地把结果赋给变量以保留其值。
while(<FH1>&&<fh2>){...}#错误:两个输入都舍弃
if(<STDIN>){print;}#错误:打印$_原先的值
if($_=<STDIN>){print;}#有小问题:没有测试是否定义
if(defined($_=<STDIN>)){print;}#最好
当你在一个$_循环里隐含的给$_赋值的时候,你赋值的对象是同名全局变量,而不是while循环离得那个局部的。
while(local $_){print;}#使用局部$_
当循环完成后,恢复到原来的值。你也可以避免这些事情的发生,只要定义一个文本变量就行了:
while(my line=){print $line;}#现在是私有的了
在列表环境中使用行输入操作符,则返回一个包括所有其余输入行的列表,每个列表元素一行。
while循环的条件总是提供一个标量环境。
在一个尖角操作符里使用空(null)文件句柄是一种特殊用法,它把所有你在命令行上提到的所有文件的所有数据行都交给你。
3、文件名聚集操作符
尖角操作符中的字串不是文件句柄名或标量变量,它就会被解释成一个要聚集的文件名模式。这里的文件名模式与当前目录里的文件名进行匹配,并且匹配的文件名被该操作符返回。
@files=<*.xml>;你也可以写成:@files=glob("*.xml");
如果你想修改你所有的C源代码文件的权限:
while(glob "*.c"){#等效于while(<*.c>)
chomd 0644,$_;}