Perl

#!/usr/bin/perl    //第一行 ,表示用什么程序来执行一下命令:

注解 #

输入:<>  //从控制台输入

输出: print


[julia@oc2365548284 backup]$ more julia.perl 
#!/usr/bin/perl
#$a='a';
#print $a;

print "this is my first perl program\n"; //  “\n”表示换行。
$a=<>;
print $a;
exit
[julia@oc2365548284 backup]$ ./julia.perl 
this is my first perl program

man perl

www.perl.org

www.cpan.com  下载perl的模块


第1:第一章 Perl的概述
二 变量
perl 有4种变量

scalar : $  纯变量,标量,就是简单变量

数组:@
Hash:%  散变量
文件:大写来标识

第2:第二章 Perl的简单变量
1.简单变量
$  变量区分大小写
perl变量长度没有限制
perl里面没有关键字
$if   $_重复

注意:perl里面有一些内置的变量,

2 数值变量

整数 ,浮点数

直接把整数,或浮点数 ,或者字符串,直接赋值给变量,变量就知道你是整数,还是浮点数,还是字符串

整数的变现形式:12     12e2 (1200) e 不区分大小写 ---科学计数法   -12
                          
1_200_000 可以用下划线分割数值,数值太长不方便查看,perl会自动忽略下划线。不一定是三位分割。
012 八进制,值为10
0x1f  十六进制 ,值为31 ,f不区分大小写。

3.浮点数
11.4
-0.3
.3
3.
5.4e2    =540  

4.注意的地方
整数的限制:15位有效数字,16位后截断  3232_3232_3232_3239 后面的9会变成0
浮点数的精度:指数范围-309 -308  太小显示为0,太大显示为1.#F  (windows下)

三 字符串变量

一般由双引号,或单引号标识的一组字符组成
最少0个字符,最多可以占满内存 ,末尾不含null('\0').

“”双引号  --可以变量赋值,
$var="str";
print "this is $var";->"this is str"

变量替换的时候遵循的是最长匹配原则,比如
$str  ,$string都存在时,匹配$string
要匹配$str时,要用${str}  要匹配短的话,要用{}括起来。

[julia@oc2365548284 backup]$ more shot.pert
#!/usr/bin/perl
$str="short";
$string="long";
print "match longest $string\n";
print "match longest ${str}ing\n";
[julia@oc2365548284 backup]$ ./shot.pert
match longest long
match longest shorting
[julia@oc2365548284 backup]$

第3:第二章 Perl的字符串变量
转义字符
\n 换行
\a 响铃

\的作用
转义字符
取消字符含义      \$var  取消$的含义,可以输出$
                         \"  会输出“
加了反斜杠 就输出后面的符号的作用

‘单引号的作用---不进行转义


第4
$i=1+1
+
-
*
/
** 幂
%取余,操作数为整数,否则要截取 1.5 直接取1。取余操作第二数不能为零,因为除数不能为零
-负

当一个字符串参加运算,需要转化为整数,如果不能转化为整数则值为0.



2 数值比较符号
<
>
==
<=
>=
!=
<=>比较,比较的结果
$a<=>$b
当$a>$b时,值为1,
当$a<$b时,值为-1
当$a=$b时,值为0.

自动把操作数化为整数,不能转化为整数的则为0
浮点数不精确,不要把值相近的数比较


3 字符串比较
lt
gt
eq
le 小于等于
ge大于等于
ne 不等于
cmp比较

比较是按照字母表的顺序比较,

数字<大写字母<小写字母 a小——z大

字符串比较从左向右比较  。'azz' <'bc' 先比较a 和b  .a
当一个字符串是另一个字符串的前缀时,长度长的为大  。dog
自动把操作数转化为字符串 123 lt 45 转化为字符串 '123'lt'45'  1比4小,然后就不比较了


数字比较和字符比较的运算符不同就是为了区分是数字比较还是字符比较


4.字符串连接(加), 重复(乘)
“.”   点来表示连接    :连接:$a='a'.'b' 结果是:‘ab’    效果跟print $a$b  print$a.$b一样
“x”重复  x前后有空格(为了和变量名分开) ‘a’ x 5=‘aaaaa’  。如果次数小于1的,结果就是空


5 逻辑运算符

&&(and) ,  ||(or) , !(not), xor
先算左边,后算右边


位运算符

&与
|或
~ 非
^ 异或
<<左移动
>>右移

操作数都是二进制的整数,如果是小数则截取为整数,如果是负数会变成无符号的整数。

<<  移位后空位补零,值就为原来值的二倍
>> 右移,首位补0,值就为原来的一半。


赋值运算
=
+=
-=
*=
/=
%=
**=
&=
\=
^=
.=

$a+=1   => $a=$a+1
连等 $a=$b=3;=>$a=3,$b=3;
混用($a=$b)+=3;=>  $a=$b;$a=$a+3 ;不建议使用




自增,自减运算

不用在变量两边都用此操作符号 :++$var-- ;容易引起歧义不要这样使用
不要在变量自增/减后在同一表达式中再使用:$var2 =$var1++$var1
字符串自加 ,当z ,Z,9时进位。$a='az'; $a++; =>$a='ba';
不能用于字符串的自减,当$a--时,按照数字运算,字符先转化为0再自减。
如果字符串含有非字母数字符号,或数字位于字母中时,自增也先化为0再自增。
$a='ab*c';$a++;$a='ab5c';$a++;   结果都是$a=1;

预增$b=++$a,$a先自增再赋值,
后增$b=$a++:$a先赋值再自增。

$a=1;$b=++$a;=>$a=2,$b=2;
$a=1;$b=$a++;=>$a=2,$b=1;
以上自增自减只能用于单一变量,不能对运算后的变量进行。如($a+$b)++

","逗号的使用
$a+=1,$b=$a;=>$a+=1;$b=$a;
只有当两个语句关联紧密时才使用。没有其他什么作用

条件运算符

条件?真 :假
条件为真 执行前面的运算,条件假执行后面的运算。

$result = $var == 0? 14:7;
var这个变量等于0的话(为真),就把14赋值给result这个变量。否则为7;

条件表达式位于等号的左边;
$condvar == 43? $var1:$var2=14;

condvar等于43吗,如果是,就把14赋值给$var1,否则就把值赋值给$var2
等价于 $condvar == 43? $var1=14:$var2=14;后面的好比较直观。


第八

运算符的优先级

一般:自增自减最高,单操作数高于多操作数 ,数字运算>比较运算>位运算>赋值运算>逻辑运算

单操作数:例如取非,

赋值运算一般是最低的

数字运算:幂>*/>+-
比较运算:<>高于== !=

有个优先级表


结合性
表达式中多个符号是同一个优先级别的时候,先左后右边

右结合性的有:
$a=$b=1;$a=$b+=1;从右边往左边

不确定的时候使用()


第9

条件语句
if条件语句
unless条件语句
while循环语句
until循环语句
for循环语句
foreach循环语句
循环控制语句

语句的种类


执行重复任务---循环执行,循环语句
需要作出大量判断-----按条件执行,条件语句

if(表达式){语句}
elsif (){}
......
else{}
perl忽略空格所以可以断行


unless 语句
unless(表达式){语句}
执行语句除非表达式为真
为真的时候退出。表达式为推出条件。

while(){}
表达式为真时候执行,是执行条件
认真设置条件有为假的可能,否则无法退出。

do{语句}while(表达式)
至少执行一次


until(){}
执行语句,直到表达式为真停止。

do{}until()
至少先执行一次语句,再进行条件判断

for 循环
for(表达式1;表达式2;表达式3){}

表达式1:初始化变量,只在第一次循环时执行
表达式2:每次循环时比较,为真时循环。
表达式3:每次执行完循环才执行,改变控制变量的值,一般为自增。最后使得表达式2为假,循环推出。

三个表达式都可以省略但是;必须保留,形式for(; ;)

for ($line=<>,$count=1;$count<10;$line=<>,$count++){print $line;}


foreach $w(列表或者数组){语句}

()内可以是数组@a,或列表(1,2,3)
$w可以是新变量,也可以是已使用的变量,在循环结束后变量值恢复
每次循环把列表或数组中的值依次赋与$w,直到列表为空。

foreach的形式
foreach $a(@a){} 一般形式。
foreach $a(1,2,3,4){}列表

foreach(@a){} 省略循环变量,缺省为$_

print; 默认打印$_

第十
循环控制

last 退出循环
next 循环中next下的语句本次不执行。对于for要执行变量自增

标记语句块
标记语句库只能有字母和下划线组成,一般为大写,
定义在循环语句前
跳转到标记:last|next|  LABLE;
一般用在嵌套循环的内层跳到最外层。其中last是退出最外层循环,程序继续向下走,不是跳转到最外层再执行。
OUT:while(){
while(){.......last OUT;
         }
    }
last 只能推出里面那个while的循环,加上 标签后OUT后,能跳出外面那个循环。继续往程序下面执行
last 跳出OUT所标识的语句块。

单行条件语句与循环语句:语句在前,条件在后。
print $a if $a==0; 但是先测试条件再执行语句。

用||,&&的条件语句:$a==0&&print $a  ; a是0 后面就不会执行了。
                          open(F,'file')||die "can't open";

die函数:输出信息后退出程序
warn:输出信息后不推出程序,只起到警报的作用
$!  内部变量包含错误代码
$@ 内部变量包含错误信息


总结:

条件语句
if(){}
elsif(){}
.......
else{}

单行语句if(表达式);
|| or && and形式
?:形式
unless(){}
单行语句unless(表达式)

while,until 循环
while(){}

do{}while()

单行语句while()

until(){}

do{}until()

单行语句until()


第11

列表与数组

列表
(1,“a”,2.3,$a,$x+1)
元素可以是任意类型,变量,表达式
空列表:()
单元素列表:(2)。值与2不同。

数组的另一种表达形式,qw用空格分开元素,perl会把里面每一个元素都当作字符串来对待。
qw(1 $a str)
qw用法同q ,qq。()可替换成其他符号<>


作为逗号运算返回最右边的元素,$a=(1,2) $a=2  把列表赋值给一个变量,变量会返回最右边的值。


范围表示的列表
(1,2,3,4,5,6)-->(1..6)
 
(1,2,3,4,5,6-->(1,2..5,6)部分使用范围

(3..3)-->(3)首尾一样不能增
(2.4..5.3)-->(2.4,3.4,5.4)小数一样

(4.5..1.6)左边值大于右边值则为空

("aa".."ad")-->("aa","ab","ac","ad")同样可用于字符串增加。$month=('01'..'31')规则同字符串自增。

($a,$a+3)-->设$a=3,则为(3,4,5,6)同样可用于变量表达式。


数组--列表的存储

列表不能存储,但是数组可以

把一个列表存储起来就赋值到一个变量里面
@a=(1,2,3)
与$a是不同的变量

数组的元素 $a[0]
$a[-1]倒数第一个元素
$a\[0]   ${a}[0] $\{a}[0]均表示字符串$a[0]

@b=<>;从控制台读入一行赋予元素,最后用ctl-d结束输入

改变元素的值:$a[1]=3;@a=(1,3,3,4);

超出数组大小的元素:$a[5]=6;@a=(1,2,3,4,"",6)数组自动增长,原来没有的元素值为NULL.

读不存在的数组为空:$x=$a[7];


数组元素的读出

单个元素 $a=$a[1];

@a=(1,2,3);($x,$y,$z)=@a; $x=1,$y=2,$z=3 ,数组的元素赋值给每个变量,多余的变量为空,变量不足后续数组元素忽略

数组的读取:

$a=@a;$a为数组的长度。($a)=@a;为取第一个元素。
$a=(1,2,3),返回最右边的元素,$a=3;

$#a数组最后一个元素的索引,加1即为长度

数组的输出:print @a ;输出结果是元素相连
                    print "@a";元素间加空格。


第11  数组与列表
数组元素:
访问数组的部分元素,不是单元素,不是全部
@a=(1,2,3,4,5)
读出@sub=@a[0,1,3] @sub=(1,2,4)
给数组赋值:
@a[1,3]=("a","b")  @a=(1,"a",3,"b",5)


数组操作函数
sort{代码块} @array :默认把数组都认为是字符
@_perl的内部变量来代表数组。

@a=(1,12,4,8,20);
print sort(@a); //默认是当作字符来比较的
print sort{$a<=>$b} (@a); //当作数字来比较。 $a 和$b 是perl内置的一个全局变量。

revers @a反序排列


第12
数组函数
join('连接符',@a)把数组连接成一个字符串

split (/模式/,分割串,长度)
模式:缺省为空格,可省略

$s="a,b,c";@a=split(/,/,$s);@a=('a','b','c');

wantarray():当期望返回数组时为真,返回简单变量时为假


数组函数之三
splice:

@ret=splice(@a,skip,length,@newlist)

对@a数组操作,跳过skip个元素,然后开始用@newlist替代length个元素。
@newlist和length可以不等长,无论@newlist多长,

当length=0时为插入。splic(@a,-1,0,@n) 在末尾追加
当@newlist为空则为删除。
当length 和@newlist省略时为其后的全部删除。splice(@a,3);

@found=grep(/pattern/,@search)对@search的每一个元素搜索匹配模式,匹配的元素返回到@found数组中。

map(expr,@list)对@list每个元素进行expr运算,返回运算后的数组。元素用$_代表。map($_+1,(1,2))->(2,3)


二位数组

二位数组用的比较少 太好,不用看了

列表

数组

@a在简单变量上下文中为长度,$#为最后元素的索引。
数组函数



第13集
关联数组



第16集
文件

文件描述符:访问文件时用来代表文件的数字,它系统资源,系统限制打开的文件描述符的数量。
perl中只有在某些系统调用时才使用它。
文件句柄:perl 使用文件句柄代表文件。

文件描述符与文件句柄相互转化:
文件句柄=>文件描述符:fileno(F);返回文件描述符。F:是文件句柄
文件描述符=>文件句柄:open(F,">&n);文件描述符号是系统使用的,可以把文件描述符转化成文件句柄然后给perl使用
open(F,">&n) ;转换。

文件变量:
文件存放在磁盘上,永久存储数据,用于读写访问,访问前要先打开文件,结束后关闭文件

open(Handle,">filename")||die $!;成功返回非零,失败返回零。
handle:文件句柄,文件变量。用来代表操作的文件。perl使用的文件句柄。操作系统上的文件,要通过open函数转成文件句柄以后才能给perl打开。
open(F,"+> t.txt") 打开文件t.txt

缺省打开的句柄STDIN,STDOUT,STDERR(这三个文件不用open可以直接使用,默认就是打开的)。文件描述符为0,1,2.不用调用open就可以直接访问呢。
STDIN:键盘输入,控制台。
STDOUT:屏幕,显示器。
STERR:错误输出,显示屏。

文件访问模式:
文件用于读还是写,在第二个参数前的>位置。

只读:<或省略,如果文件不存在则打开失败。如open(F,"file");
open(F,"
只写:> 为写打开文件,文件不存在则创建,文件存在则清楚内容再写。

追加:>> 文件不存在创建,文件存在不清楚内容。

------------一下不常用,可读可写不常用----------------------------------------------------
读写:+< ,可读可写,文件不存在则失败,不清楚内容,不追加,直接覆盖原文件。

读写:+>,可读可写,文件不存在则创建,文件存在清除内容再写。

读写:+>> ,可读可写,文件不存在则创建,文件存在则追加。

---------------------------------------------
管道: |

open(F,"| cat > hello");把文件F的输出(print F $a)作为|后的输入。----把文件F的输出作为 输入 并定向到 hello
open(F,“comm|”);把comm的输出作为F的输入。comm为命令。

文件名:最后的一个参数

可以用变量代表文件名字


文件
open() || die "error $!"   open成功的话,后面的die才执行

die($errsr); 向stderr输出错误信息串,然后推出程序
不想推出的话,可以使用下面的程序。

warn($errstr);向STDERR输出信息,不推出程序。

$!最后一次错误信息。与die,warn结合使用--到底是什么出错了 加$! 在$str中--------很好啊。

die($str)中$str中最后不是\n时,输出自动添加at程序名line行号。
如die("can not open");输出can not open at myprog line 13 .
如果加上\n“can not open\n”就不会自动添加错误行号。

close(F);close函数跟文件句柄。
文件缓冲,close后才会写到磁盘,perl程序打开的文件都很小,不会立即写,所以不close ,看不到tail -f wenjia

无文件缓冲,写的很频繁的话,影响性能。文件缓冲是系统设置的。

文件缓冲关掉, $|=1 表示无缓冲, 只对当前文件有效。

除了open还有其他函数打开文件

可以用变量代表文件名字
$a="F";open($a,"mode","path") mode:文件访问模式。

sysopen
。。



文件打开后就可以读文件了

$line=读一行到line,            --------最常用的方式
如果没有<>.缺省是读到$_中的。
怎么区分是一行一行的呢
$/='\n'   $/:行分割符号,遇到它则认为是一行结束
还可以把行结束符$/设置成为其他字符串:
行包含$/(每行都会包含一个分割符号,例如##,)可用chomp($s)去除此标志。

@a=文件所有行全部读出来放在数组里面。
以上是最常用的读文件的方式

还有一下
read(F,$in,len[,$offset])读入$in
F:文件变量打开
$in:是读到这个文件变量里面
len:读多长
[,$offset]:从什么位置开始读。


文件尾检测
eof 和eof()
假设@averg("file1","file2");--------标准输入的默认数组,全局变量,一旦赋值原来值丢失。
while($line=<>){if (eof){print 'end of file'}}
读完一个文件输出 "end of file",然后继续打开下一个文件。
if (eof()){print "end of file"}所有文件都读完才输出一个end of file

写文件

print F ( "str");  
F文件句柄,后面有空格,省略F为STDOUT。
str输出内容。可用单引号,可用单引号‘ ,不进行变量替换,不加引号,计算出变量的值再输出。把str的内容写到前面的文件句柄里。

文件权限

chmod (mode,filelist)
chown(userid,groupdi,filelist)
umask(maskval)

文件指针:实现随机读写。
tell(F) 当前的位置,读到哪一行就计算到:计算的是字节数。可以用来计算一个文件有多少个字节。
seek(F,distance,pos);定位到指定位置
pos=0 相对于文件头部,1相对当前位置,2相对文件尾。
distance 相对pos表示的位置多少字节。

示例:
#!/usr/bin/perl

open (F,"+
#以读写方式打开文件,t.txt
$a=;
pring "line 1=$a";

#print tell(F);
#print F "a" .$/;
#seek(F,3,0);
#$a=;
#print "line 2=$a";
print F "a" .$/;
close(F);



#!/usr/bin/perl
open (F,"+ #以读写方式打开文件,t.txt
#F是文件句柄:perl是以文件句柄来识别文件的,open()函数用来打开文件,+<表示以读写的方式打开文件。
@a=;
print @a;
open (H,"+ while($line=){if(eof){print  "end of file\n"}}; #一行一行的读文件,如果文件读完,就输出end of file到标准输出,
#print H ("写文件到文件句柄");  #把str写到了H文件里了 。文件写入操作。
print tell(H);#可以查读了多少个字节。
print "\n";
###seek 从哪里开始读,seek(F,distance,pos) ,pos=0 相对于文件头的位置,1当前位置,2文件尾。distance相对位置。
seek(H,3,0); #相对于文件头部,第3个距离开始读出这一行,空格不算在内。
$b=;      #把读到的输出给变量b,为什么是变量不是数组,可能是要一行吧。
print "line 2 :$b"; #把这一样打印出来。
#print F "a".$/;
close(F); #文件打开以后要关闭,才能释放缓存。
close(H);


文件测试

if (-e "filename"){print STDERR ("file\n");} 问家是否存在。
-b 是否为块设备
-c 是否为字符设备
-d 是否为目录
-e 是否存在
-f  是否为普通文件
-g 是否设置了setgid位置
-k 是否设置了sticky位
-g 是否设置了setgid位
-l  是否为符号连接
-o 是否拥有该文件
-p 是否为管道
-r 是否可读
-s 是否为非空
-t 是否表示终端
-u 是否设置了setuid
-w 是否可以写
-x 是否可执行
-z 是否为空文件
。。。。。。

文件状态

stat(file):参数可以是文件名字,也可以是文件句柄

文件操作

@a=glob("*.txt") ,返回与模式匹配的所有文件目录,调用shell执行所以文件多会报错。

truncate (filename,lenght)  :将文件的长度减少到length字节,如果文长度已经小于length,那么什么也不做了。
其中filename可以是文件名字,也可以为文件变量。

rename(oldfilename,newfilename)成功返回=1,不检查new是否存在,可能破坏文件,
-e "file2" || rename(file1,file2);

目录操作
和文件相对应,打开关闭读写
opendir(dirvar,dirname) ---dirvar 目录变量,需要大写,要打开目录,查看某目录中的文件列表时也要先打开,再操作,再关闭。

dirvar:目录变量,dirname:目录名字,成功返回真值,失败返回假。

closedir(dir):关闭目录。

readdir(dir):打开目录后就是度目录了
对数组返回所有文件和目录,对简单变量每次返回一个文件或目录。不含当前路径。

chdir(dir):改变目录,运行结束返回原目录。

telldir(dir):当前目录位置。
seekdir(dir,loc);到指定位置

mkdir(dirname,permission):创建目录(当前目录,访问权限)

rmdir(dirname):只删除空目录,成功返回真,失败返回假。

正则表达式

最有特点的地方perl

比较有用的的正则表达式:

字符匹配,选择,转义符和定界符--------这三个最常用的,其他的不怎么用。

引用匹配
匹配变量
匹配选项
匹配符的优先级
扩展匹配

其他匹配
替换
翻译


正则表达式 (模式匹配)

/def/  即匹配def
匹配操作符号:
=~ 匹配为真
!~不匹配为真

$question="expleaseding"
$question=~/please/  #匹配运算,匹配为真,
$question!~/edit/     #不匹配为真。

正则表达式一般都是用于条件判断

if ($question=~/please/){ print "Thank you for being polite!\n"}
else{print ("That was not very polit!\n");};

grep :正则表达式只对简单变量匹配,如果是数组
@a=~/abc/   //自动把数组认为是简单变量了,所以自动转化成数组的长度了。

对数组进行匹配用grep()函数
grep(/abc/,@a);对数组中的每个元素匹配。

split(/abc/,$line)根据模式匹配分割字符串。$line 是一个长串,中间有abc ,分割出来的字符串数组就不包含abc了。


模式匹配的3种类型:
m//模式匹配,缺省是模式匹配。
s///匹配并替换,
tr///遂一替换,翻译

模式匹配:字符匹配

+  *  ? {}

+:一个或多个相同的前导字符:
如:/de+f/  ---->def  deef deeeef
/d[eE]+/  -> de  dee  dE dEE  deE  dEe 相当于 /[eE] [eE] [eE] ...../  [] 表示可以大写或者小写都可以。

匹配0个、1个或多个相同字符

?匹配0个 1个该前一个字符
. 匹配除了换行外的所有单个字符,通常与*合用 .*所有任意数量字符。

匹配指定数目的字符
{}指定所匹配字符的出现次数。如;/de{1,3}/   -> def deef deeef

/de{3}f/ ->deeef
/de{3.}f/ ->匹配大于等于3个e

字符匹配规则
贪婪规则:匹配多的字符
/ab+/  ->abbc 匹配的是 abb,而不是ab ,尽可能多的匹配

/a.*?c.*d/;  不尽可能多的匹配 ,可以加一个?

懒惰规则:匹配模式只要找到一个就停止。不再继续匹配。


匹配模式之二:选择

[]  ,匹配一组字符中的任意一个 /a[35654343]c/  ,
[^ ].除了[]里面的的字符之外的字符。
[0-9][a-z][A-Z]  匹配任意字母或数字

|  ->/def|ghi/ 匹配def 或ghi

转义符号

\d 任意数字
\D除了数字外的任意字符[^0-9]
\w 任意单词字符[_0-9a-zA-Z]  ;单词是什么意思呢,下划线,数字,字母,但不包含@,$
\W 任意非单词字符
\s 空白,[\r\t\n\f] 比较常用,因为经常取一个文件,到处都是空格啦,换行啦,跳格啦

\S  非空格

定界符号
^ 或 \A 仅匹配字符串首,
$ 或\Z 仅匹配字符串尾
\b匹配单词边界
\B单词内部匹配
/^def/只匹配行以def打头的字符串
/def$/只匹配以def结尾的字符串。
结合起来/^def$/只匹配def

示例
\b   /\bdef/  ->def  defghi  $defghi $不认为是单词,被人认为是空格但是不匹配abcdef
/def\b/  ->abcdef

匹配选项

g  匹配所有的模式

i 忽略模式大小写  /de/i


格式化输出 ------用的不多

子程序即一段分离的代码,
放在程序的开头或是结尾
以sub开头  //子函数的关键字是sub

sub   自程序名字{
statements;
}
------------------------
sub sub_name{
       print "Hello world";
}
这样就定义了一个sub_name的子函数。
--------------------------------
调用的时候我们一般这么来做:
&sub_name:
注意&符号,如果不加&大部分情况下也没有问题,但是如果你定义的函数与perl的内置函数重名了,那么不加&的话Perl会调用它的内置函数,如果加了&会调用用户子定义函数。

-----------------------------------------
子程序的返回值
我们可以用return来返回值,perl中规定:子程序的最后执行的一个语句的值将用作返回值。比如:

sub test_return{
1;}
这个函数的返回值就是1
如果你习惯使用return当然也没有问题
如果直接这么写:return;return后面没有什么变量,此时会返回undef

-----------------------------------------
参数传递
Perl中在子函数中一般不会写明各个参数,比如:

sub test_sum{
my($fist_num,$second_num) =@_; #来读取参数。这样可读性更好点。
return $first_num+$secons_num;  
}
$first=1;
$second=2;
$ret=$test_sum($first,$second);
print "ret:$ret";

子程序test_sum接受到的参数放到了一个数组中,数组的名字固定,叫:@_,他的第一个元素就是$_[0],第二个元素就是$_[1],不过我们为了可读性更好一些,一般都是my($first_num,$second_num)=@_;这样来取参数。
当然,有的时候参数很多,用上面的方法就不好办了。@_是个数组自然就可以适用shift,pop等方法一次取一个元素来循环处理。

---------------------------------------

带有原型说明的子程序定义:
sub 子程序名字($$){  #说明这个子函数需要两个简单变量作为参数
statemetns;
}

()中的符号表示参数的类型。一个字符表示一个参数的类型

$表示简单变量
@表示列别
%表示散列
&匿名子程序
*引用
\ 前加\为强制类型一致。如果传递给子函数的变量类型不一直perl会强制类型转换的。
;分隔必须的参数和可选的参数

\@$;$  表示第一个参数为列表,第二个参数为简单变量,第三个参数简单变量可选。


函数返回值

参数传递

&sub1 (&number1,$number2,$number3);# 会把此时实际的调用的参数取值都放在@_中

sub sub1{
my ($number1,$number1,$number2)=@_;  #会把主程序上面即调用这个子函数的的参数值赋值给这里的参数
}

my $number1=shift;
my $number2=shift;
my $number3=pop;

所有的参数在数组@_中,每个元素为$_[0],$_[1],@_,$_[]为局部变量,当子程序再调用子程序时,@_不改变。会新生成另一份@_.

改变@_内元素的值如$_[0]会改变主程序的参数值。
shift pop一次取一个参数。
对@_操作。shift取第一个参数,然后删除,pop取最后一个参数再删除-
shitf比较常用。
pop比较少用。。

my $a 定义的局部变量
在程序块内有效;包括在程序块内定义的程序块,但是调用的程序块不可以。
程序块:{} 用大括号扩起来的都是程序块,大括号里面可以再放大{}。
my($a,$b) 没有括号就不一样

调用子程序
标准调用 $subname(1,2);
--无论子程序定义在什么地方都可用&调用子程序
常用调用:subname(1,2);
--只有在调用之前已经定义的子程序才可以省略&
向前引用:先声明,再调用,再定义。
sub subname;
subname(1,2);
sub subname{}
用do调用 do subname(1,2);相当于&subname(1,2);

递归调用
子程序自己调用自己的时候,就成了递归子程序
有两个条件:

预定义的三个子程序---有时候很有用的。
在某些时候自动执行的。
begin
end
autoload

----------------------网上搜来的---------------------
函数可分为系统函数和用户函数。
用户函数
  用户函数又称为子程序(subroutine),在perl中用下面的结构来定义用户函数:

sub 子程序名{
       语句块;
}

这里的子程序名与变量的取名规则类型。

以显示欢迎的程序为例:
sub say_hello {
print "你好,欢迎dear";
}

用户函数的定义可以位于程序的任何位置,比如说放在文件的末尾,如果两个子程序使用了相同的程序名,后面的子程序将覆盖前面的子程序。

用户函数中的变量默认为全局变量,与其他程序共享。

用户函数的调用:
通过在子程序前加"&"调用,可在任一表达式内调用。子程序中可以再调用另外的子程序。

调用用户函数产生的结果称为返回值(return value).返回值是每次调用函数中最后一个表达式的计算值。以加法函数为例子:

sub add_a_b{
$a+$b;
}
函数最后一条表达式是$a+$b,故返回值为$a+$b。以下是调用情况:

$a=5;
$b=6;
$c=&add_a_b;  #$c的值为11
$d=5*&add_a_d;#$d的值为5*11=55

在perl中,如果函数调用后面跟着一个用括号括起来的列表,则在函数调用期间该列表将被自动分配给以 @_命令的特殊变量。函数可以访问该变量,从而确定参数的个数及赋值。

仍以加法函数为例:

sub add_a_b{
$_(0)+$_(1);
}
$c=&add_a_b(5,6);#$c的值为11
$d=5*&add_a_b(2,3);# 5*(2+3)=55

--------------------------结束--------------------------

--------------------------搜集--------------------------
子函数的作用域是指在程序中能够看到它的位置的范围,子例程是全局的,可以放在脚本中的任意位置,甚至放在其他脚本文件中。当使用来自其他文件的子例程的时候,应当使用关键字do ,require,use 将它们加载到脚本里,若要调用一个子例程,用户可以在子例程前加&符号,或在子例程加do关键字,也可以在子程序名后加一组括号。如果使用了向前引用(forward reference),在调用子程序时就不需要提动&或者括号。


参数传递:

函数把接收到的参数保存在特殊的perl数组@_中,其中每个元素$_[0],$_[1],...称为一个参数。
不论参数是标量型还是数组型的,用户把参数传递给子例程时,perl默认按照引用的方式调用他们。
用户可以通过改变@_数组中的值来改变相应实际参数的值。

pop()函数,将数组的最后一个元素取出并返回:
@array=5..9
$fred=pop(@array);  $fred得到9,@array现在为(5,6,7,8).####

----------------------------------------
local与my区别

local函数声明的变量不但在创建它的块中可见,而且对于任何从该代码块调用的函数中也是可见的。
与local函数声明的变量不同,my函数声明得到的所有变量都只在声明它的子函数中可见,对于该子函数调用的任何其他子函数都是不可见的。


内部->外部
1)my和local都只在一个block里有效,出去就失效;
2)但是local的变量可以继续在这个block中调用的子程序中存在;
3)如果有与外界同名的变量,两者在block退出后都不影响外界同名变量。



第30  perl引用
perl里面比较复杂的地方

引用的本质就是指针,存放的是其他变量的地址。
$ref---里面存的是$a的地址(存放的是一个变量的地址)


引用可以指向简单变量,常量,数组,散列,函数.

引用的创建

用\创建引用,\的意思就是取这个变量的地址。

简单变量的引用:$var=22 ;$ref=\$var;
常量的引用:$ref=\3.4
数组的引用:$ref=\@ARGV;
散列的引用:$ref=\%ENV;
函数的引用:$ref=\&func;

指针就是地址,通过指针可以访问该地址处存储的数据。如果指针指向了无效的地址,就会得到不正确的数据,
通常情况下,perl会返回null值,但不该依赖于此,一定要在程序中把所有的指针正确地初始化,指向有效的数据项。

perl引用和数组

#--------------------引用/or 指针-----------------

应用的创建 $ref=\$var  ,把$var的地址给了 $def

数组的引用$ref=\@ARGV
散列的引用$ref=\%ENV
函数的引用$ref=\&func;---注意后面不能加(),否则就变成调用函数了。

文件的引用:$ref=\*STDOUT;
匿名数据的引用:$ref=[1,2,['a','b']];匿名数组返回的都是个引用。
匿名散列的引用:$ref={'a'=>1,'b'=>2};
匿名函数的引用:$ref=sub {print "anonymous func";}

引用的使用
简单变量的引用 $$ref
数组:@$ref 元素 $$ref[0] 或者 $ref->[0]
散列:%$ref 元素:$$ref{'a'}
函数:&$ref(a,b);
文件:$ref

引用会改变指向的变量的值:$a=1;$ref\$a;$$ref++ ====> $a=2;

引用可还原为普通变量:$ref=1;

ref()与引用的显示

这个函数,显示这个是否是引用
ref($a):判断$a是不是引用。

print $ref --->返回的是类型和地址。--打印的引用。

类型通配typeglob

符号表;存的是变量的名字
包的变量就是存在符号表里的,每个变量一个入口,每个入口包含所有类型的名字
如:$name ,@name,%name,&name,文件name,格式name。这个入口为类型通配*name->代表所有的类型
这些变量都在一个路口里。

legitimate legitimate legitimate legitimate legitimate legitimate legitimate



$value=10;
$pointer=\$value;
print "\nPointer Adress $pointer of $value\n";
print "\nWhat Pointer *($pointer) points to $$pointer\n";
##可以看出$pointer给出地址,$$pointer给出$variable的值。
@ARGV=(1,2,3,4,5,6);
$pointer=\@ARGV;
print "\nPointer Address of ARGV=$pointer\n";

$i=scalar(@$pointer);#调用函数scalar()获取数组的元素个数
$h=@ARGV;#可以使用@数组名字,来计算数组长度。如果用指针必须用@$pointer的形式指定其类型为数组,$pointer给出地址,@符号说明传递
#的地址为数组的第一个元素的地址。
print "i is $i\n h is $h\n";

for ($i=0;$i<@$pointer;$i++){
print "$i:$$pointer[$i];\n"; ##使用$$pointer[$i]列出所有元素。
}

----------------perl 包和模块----------
每个包都有自己的一个符号表,它是一个哈希表;关键字是包中的变量名,文件句柄,目录句柄以及子函数。

perl程序把变量和子程序的名称存储在符号列表中,Perl的符号表中名字的集合就称为perl包(package).

包的定义:

名字空间:
 使代码段不发生冲突而用以分开代码的空间:
使变量和函数分为不同区间的标记,不同空间中的变量或函数可以同名。
使用变量要带上名空间的前缀$name::a

包定义

package mypack
用包来实现名字空间。
从package mypack之后定义的变量和子程序,都是属于这个包的,直到另一个package语句为止。

符号表
包的变量和子程序都存储在符号表中。
%package : :------来引用符号表。

-------------------------------------网上搜集来的-------------------------------



第11章:引用

  • 引用一律声明为标量类型(即$开头的命名变量),使用\运算符取引用
    • 对引用变量的修改等同于对引用指向实际数据的修改
    • 取变量引用:my $scalar_r = \$scalar;
    • 取列表的引用:my $array_r = \@array;
    • 取哈希的引用:my $hash_r = \%hash; 
  • 通过引用解决列表无法嵌套的问题:

 

my @array1 = (10, 20, 30, 40, 50);
my @array2 = ( 1, 2, \@array1, 3, 4);

 

  • 因为\@array1本质上只是一个标量,所以列表不会被扁平化,依旧保留了嵌套层次
  • 对匿名列表的引用:将列表的()替换为[]:my $array_r = [1, 2, 3, 4, 5];
  • 对匿名哈希的引用:将哈希的()替换为{}:my $hash_r = { apple => "pomme", pear => "poire" };
  • 对引用变量解引用通过{$var_r}实现
    • 列表

 

my @array = (1, 2, 3, 4, 5);
my $array_r = \@array;
my @array2 = @{$array_r};  #拷贝了数组

 

    • 哈希与列表类似
  • 对于数组引用,可以将${$ref}简记为$ref->,例如可以将${$ref}[2]简记为$ref->[2],而将${${ref}[2]}[1]简记为$ref->[2]->[1],并进一步简记为$ref->[2][1]
  • 使用undef销毁一个引用:undef $ref; perl对于引用指向的数据维护一个引用计数,当计数减到0时引用指向的数据被销毁,内存空间被释放
  • 使用引用使得表示复杂的数据结构成为可能。这些数据结构包括矩阵(多维数组)、链表、树、图等。
  • 一些思考:C/C++的引用主要为了传地址。与C/C++中的指针、引用不同的是,perl引用除了传递地址外,还是perl中将标量(scalar)、列表(list)、哈希(hash)进行一般化(或者说统一)表示的机制,使用引用后,可以将标量、列表、哈希均表示为标量(因为地址本质上是一个无符号整型数,这一点与C中的void*有些类似)。通过引用,就可以解决perl中无法存储带有嵌套层次的列表、无法表示复杂数据结构的问题。

第12章:模块 

  • 模块是一个perl源代码文件,与普通的.pl源代码文件相比,模块有如下两个不同点:
    • 扩展名不是pl,而是pm(这一条并不是强制条件)
    • 最后有一句1;(或是return true;或是任何返回true值的语句)强制要求
  • do用于在perl代码中的任意位置嵌入一个.pl脚本或是.pm模块,语法是do “filename”;perl将会在@INC中的路径下寻找filename
    • 如果在main.pl中执行do ‘inc.pl’;需要注意的是inc.pl中的代码不能访问main.pl中定义的lexical变量
  • require用于在perl代码中的任意位置嵌入一个模块,语法是require “filename”;或requiremodulename;当使用requiremodulename;时,modulename形如module::submodule::subsubmodule,代表文件./module/submodule/subsubmodule.pm
    • 与do不同,require只支持嵌入模块,因此require的文件必须以返回true语句结束
    • 对于一个文件只嵌入一次,即使写了多条对同一文件的require语句
  • use用于在perl代码中的任一位置嵌入一个模块,语法是use modulename;modulename同require中的modulename
    • 与require不同,use在编译前执行,也就是说,即使use中的模块写在代码中的最后一句,也会第一个执行

 

- do require use
支持 源码 模块 模块 模块
语法 do ‘filename’; require ‘filename’;
require modulename;
use modulename;
处理次数 嵌入几次,处理几次 仅一次 仅一次
文件不存在 跳过,不报错 报错 报错
执行时间 运行时 运行时 编译时(最先处理)

 

  • @INC变量也是一个普通perl列表,可以更改,因此可以自行向其中添加路径(使用unshift或者push)
  • 模块文件的开头应该声明package packagename;packagename形如package::subpackage::subsubpackage,对于package::subpackage::subsubpackage中的函数sub1,调用方法是package::subpackage::subsubpackage::sub1
    • 注意package声明的是模块名,而require和use使用的模块名实际上是路径名,和模块名并不一样,比如说有一个模块为./m1/test1.pm,其中的package声明却是package m1::test2;该包内包含一个函数sub1,则在main.pl中应该use/require m1::test1;而在调用时则应该写m1::test2::sub1()
    • 当然,为了清晰、易管理,模块名和模块文件名、路径应该保持一致
  • 可以使用Exporter类简化包内的函数调用写法。没有使用Exporter时,必须写形如package::subpackage::subsubpackage::sub1的调用,过于啰嗦,而在写包时继承Exporter即可:

 

package Acme::Webserver::LoggerExporter;
# Acme/Webserver/LoggerExporter.pm
use strict;
use warnings;
# become an exporter and export the functions
use Exporter;
use base 'Exporter';
our @EXPORT = qw(open_log close_log write_log log_level);
则调用open_log时就可以将全写调用:
Acme::Webserver::LoggerExporter::open_log()
改为简写调用:
open_log()

 

  • 也可以在use模块时声明要导入的模块:
use Data::Dumper qw(Dumper);
# 可以直接调用Dumper()

 几个常用的包

  • Data::Dumper是将变量序列化为perl语法的字符串的包,序列化列表和哈希时非常方便
  • File::Find是一个遍历文件夹,对其中每一个文件进行处理的函数,用法是File::Find::find(\&wanted, “/home/simon/”);
    • 首个参数wanted是一个回调函数,对每个文件应用。第二个参数是执行文件夹
    • 每次执行回调时当前目录被切换为当前文件所在的目录
    • 当前目录的相对路径为$File::Find::dir
    • $_为当前文件的文件名
    • $File::Find::name为当前文件的全名(包括目录)
  • Getopt::Std和Getopt::Long是两个处理命令行参数的包,可以将形如-al的简写命令行参数解析为a和l两个参数,也可以将-a arg1 -l arg2这样的命令行参数解析为哈希映射
  • File::Spec是一个处理路径字符串的包,包括路径字符串简化、路径叠加、路径解析等
  • Benchmark是一个性能测试包,可将某一代码块重复执行若干次,测得性能参数
  • Win32是一个封装了一些Win32 API的包,包括Win32::Sound、Win32::TieRegistry等

第13章:面向对象的Perl

  • perl中并没有真正的“类”,所谓的类,其实是一个包
  • 要定义一个类,声明一个package即可:package Person;
  • 类的构造函数固定取名为new,即sub new {...}
  • 初始化类对象通过$obj = new Person();或者$obj = Person->new();
  • 构造函数sub new要点:
    • 参数表的第一个参数(@_[0])是类名,第二个开始为调用构造函数时传入的参数
    • 通过传入哈希实现类似成员变量的功能
    • 生成对象引用后,必须使用bless()函数对引用的类型进行转换
    • 最后一句必须返回生成的对象引用

 

#类定义
package Person;

sub new { #此时_@为(‘Person’, ‘name’, ‘Carl’, ‘gender’, ‘male’)
 $classname = shift; #获得类名,此时$classname为’Person’, _@为(‘name’, ‘Carl’, ‘gender’, ‘male’)
 my $self = {@_}; #将传入参数转化为哈希,$self为(‘name’=>’Carl’, ‘gender’=>’male’)
 bless $self, $classname #将引用$self转化为$classname类型
 return $self; #返回的Person对象本身是一个哈希,含有所有成员变量
}

#类使用
$person = Person->new(‘name’=>’Carl’, ‘gender’=>’male’);

 

  • Package内定义的变量为类变量,即静态成员变量(static member variable),不能直接访问,必须定义访问器(accessor, 即get/set函数)
  • 成员函数要点:
    • 名字以下划线_开头的成员函数为私有的
    • 函数的第一个参数(即@_[0])为对象引用,第二个参数开始为函数参数

 

#类定义
package Person;

sub new {...} #省略
sub _init {...} #私有函数
sub name {
 my $self = shift; #取调用对象引用
 my $name = shift; #取第二个参数
 $self->{name} = $name if defined $name; #如果传入名字,则设置名字为传入值
 return $self->{name}; #返回名字值
}

#类使用
$person = Person->new(‘name’=>’Carl’, ‘gender’=>’male’);
$person->name(‘Caesar’); #将名字设置为’Caesar’
print $person->name(), “\n”;  #打印名字,将打印’Caesar’

 

  • 对象的销毁参照11章中引用指向数据的销毁方法

后记

Perl给我留下深刻印象的地方:

  • 简捷易用的文本I/O、正则表达式使Perl成为文本处理的利器
  • 提供众多UNIX API,加上脚本语言的灵活性,Perl适合进行UNIX系统管理

个人感觉Perl中的两个难点,也是Perl的败笔:

  • 引用(Reference)
    • 列表自动一维化的机制莫名其妙。Perl中标量、列表和哈希拥有各自不同的词法标识($、@、%,列表、哈希内容均使用(),列表取值使用[],哈希使用{}),将其引用化后解引用又有一套各自不同的词法,很容易弄晕
  • 面向对象(OO)
    • Perl中的OO机制有点半残,单单是构造函数中必写的几行:
my $classname = shift;
my $self = {@_};
bless $self, $classname;
return $self;
    • 以及函数中第一句必写的my $self = shift;就让人十分讨厌,重复性劳动。如果这真的是一门OO语言,这些工作应该由编译器完成。
    • 根据Wikipedia上Perl页面的介绍,OO是Perl 5中加入的新特性,这说明Perl最早并没有被设计为一门OO程序设计语言,所以Perl 5中的OO特性可以看成是在过程式语言中进行的一种升级。比如每个函数第一句就必有的my $self = shift;就与C++中的this指针神似,只不过C++作为一种新语言革命得比较彻底,this指针是由编译器自动提供的,不必手工获取。与C++相比,Perl里的OO更像是用C语言实现的OO,说到这里,有空可以去看看《Object-oriented Programming with ANSI-C》,这本书讲了用C语言实现OO特性的各个技术细节,“通过这本书你可以明白C++, Java, Python 等面向对象语言中的类、继承、实例、连接、方法、对象、多态... 都是如何实现的. 能让你通过C来写出优美并可以重用的代码.”(以上文字来自豆瓣网友Border




你可能感兴趣的:(Perl,Perl)