第一部分 Perl基础1 介绍2 数字和字符串3 控制程序流4 列表与数组5 文件操作6 正则表达式7 哈希结构8 函数
第二部分 高级特性9 其他函数和运算符10 文件与目录11 系统之间的互操作性12 使用Perl的命令行工具13 引用与结构14 引用模块15 了解程序的运行性能16 Perl语言开发界
第1学时 介绍
# ! /usr/bin/perl对于Perl来说,代码行上的# 符号后面的一切均被视为注释行。注释是指Perl将对它加以忽略的一些东西。在某些情况下,程序的第一行上的#!是不同的。它后面的路径名/usr/bin/perl是到达perl解释程序的路径。如果UNIX程序有一行是以#!开头,后随一个解释程序的路径,那么UNIX就知道这是个程序,并且可以按照名字来运行。
第2学时 数字和字符串
4_294_296:带有下划线而不是逗号的大数字在数字前面不要使用前导0,比如010。对于Perl来说,这个数字代表一个八进制数字,它的基数是8.
Perl还提供了另一个引号机制,即qq和q运算符。使用qq运算符,就可以用qq(),而不是引号将字符串括起来:qq(I said,"Go then",and he said,"I'm gone")
也可以用q运算符来代替单引号将文本括起来:q(Tom's cat died.)
qq和q运算符可以使用任何非字母、非数字字符来标记字符串的开始和结束。这些标记称为界限符。在前面这个例子中使用了括号,不过也可以使用任何其他的非字母或非数字字符作为界限符:q/Tom's cat died./
当你在命令行上使用-w开关,或者在程序开头的#!行上使用-w来调用Perl程序时,Perl就会向你发出警告。如果你试图使用的变量值预先没有进行设定,那么当你的程序运行时以及试图使用该值时,Perl就会报一条出错消息Use of uninitialized value(使用未经初始化的值)。
特殊变量$_:Perl拥有一个特殊变量,它的值可以用作“默认值”。对于许多运算符和函数来说,该变量称为$_变量。例如,如果你只是使用输出函数本身,而不设定要输出什么,那么它将输出$_的当前值:$_="Hello, World!";print; #print "Hello, World!"
第一个字符串运算符是并置运算符,用圆点(.)来代表。并置运算符取出左边的字符串和右边的字符串,返回一个将两个字符串合并在一起的字符串.
如果Perl不能清楚地指明变量名在何处结束和字符串的其余部分从何处开始,那么可以使用花括号将变量名括起来。使用这个句法,Perl就能够找到可能模糊的变量名:$data1="1";$data12="13";print "${data1}2"; #print "12"
int int(5.6234) 返回它的参数的整数部分(5)。length length("nose") 返回它的字符串参数的长度(4)。lc lc("ME TOO") 返回它的转换成小写字母的参数("me too")uc uc("hal 9000") 返回与lc相反的参数值("HAL 9000")cos cos(50) 返回弧度50的余弦值( . 964966)rand rand(5) 返回从0到小于该参数值之间的一个随机数字。如果该参数被省略,则返回0至1之间的一个数字
第3学时 控制程序流
如果想要测试字符串是否相等的问题,可用的运算符有eq,gt,lt,ge,le,ne.
什么是真,什么是假:数字0为假;空的字符串(“”)和字符串“0”为假;未定义值undef为假;其他东西均为真。
运算符||和or并不完全相同。它们的差别在于|| 的运行优先级要高于or。这意味着在一个表达式中,||要比or更早地进行计算。这与数学表达式中乘法的计算要先于加法的情况是一样的。这个规则也适用于&&/and和!/not。
如果在if语句块中只有一个表达式,那么该表达式可以放在if语句的前面:expression if(condition);而不必写成:if(condition){ expression }
last相当于C++中的break;next相当于C++中的continue;
标号:标号的作用之一是可以在多重循环的时候,通过last与标号的组合,直接从内重循环跳出最外重循环,如:OUTER:for(...) for(...) if(...) last OUTER;
第4学时 列表与数组
列表是列表数据最简单的表示方法,它们只是一个标量的组合。有时它们使用一组括号将标量括起来,各个标量之间用逗号隔开。例如,(2,5,$a,“Bob”)
在Perl中,数组变量用一个符号(@)后随一个有效的变量名
如果列表只包含简单的字符串,而用单引号将每个字符串括起来又太麻烦,那么Perl提供了一个快捷方式,即qw运算符:qw(apples oranges 45.6 $x) = ('apples', 'oranges','45.6' '$x')这个例子创建了一个由4个元素组成的列表。列表的每个元素之间用一个白空间(空格、制表符或换行符)隔开。
Perl有一个范围运算符,可对列表进行操作。范围运算符由一对圆点(..)来表示:(1..10)以上表示一个包含1到10(含1与10)之间的所有数的列表。如果要在列表中使用若干个范围,那么只需多个范围运算符:(1..10,20..30);如果范围运算符的右边的操作数小于左边的操作数,比如(10 . .1),那么将产生一个空列表。范围运算符既可以用于数字,也可以用于字符串。范围(a..z)可以产生一个包含所有26个小写字母的列表。范围(aa . .zz )可以生成一个大得多的列表,它由675个字母对组成,从aa、ab、ac、ad...到zx、zy、zz。
数组赋值也可以包含其他数组甚至空列表,如下面的例子所示:@copy=@origina ;@clean=();在这里,@original数组的所有元素都被拷贝到新数组@copy中,但如果@copy中原先已经拥有元素,那么这些元素就会丢失。在这里,@clean变成空数组。将一个空列表(或者空数组)赋予一个数组变量,就会从该数组中删除所有的元素。
如果直接量列表中包含了其他列表、数组或哈希结构,那么这些列表将全部合并成一个大列表。
若要获得整个数组的内容,最简单的方法是使用双引号中的数组:print "@array";
若要访问一个元素,可以使用句法$array[index],其中array是数组的名字,index是你想要的元素的索引。
还可以将数组划分成分组,称为片。若要使用数组的一个片,可以使用@标号,以指明是一组元素;也可以使用方括号,以指明是数组的各个元素,如下所示:@trees[3,4,5]
特殊变量,其形式是$#arrayname。它能够返回数组的最后一个有效索引的号码。寻找数组大小的另一种方法是在期望存在标量的位置上使用数组变量:$size = @array;这将把@array中的元素数量放入$size中。
也可以为数组设定负索引。负索引号从数组的结尾开始计数,然后反向递增。例如,$array[-1]是@array的最后一个元素,$array[ -2]是倒数第二个元素,依次类推。
可以使用一个称为scalar的特殊伪函数来强制将某个东西放入标量上下文:print scalar(@foo);这个例子用于输出@foo中的元素的数量。Scalar函数强制@foo在一个标量上下文中进行计算,因此@foo返回@foo中的元素的数量,然后print函数就输出返回的数量
尖括号实际上是Perl中的一个运算符,它们的运行特性随着上下文的不同而各有差异。在标量上下文中,该运算符能够读取来自终端的一行输入。但是在列表上下文中,它能够读取来自终端的所有输入,直到读到文件的结尾,并将数据放入列表。例如:$a=<STDIN>; #标量上下文,读取一行到$a中@a=<STDIN>; #列表上下文,将终端所有输入都保存到列表a中($a)=<STDIN>; #列表上下文,但只接收第一行,剩余的全部放弃在第三个例子中, $a将收到什么呢?本学时的开头我们讲过,在列表赋值语句中,如果左边没有足够的变量来存放右边的全部元素,那么右边的多余元素将被放弃。这里,来自终端的所有输入均被读取,但是$a只接收第一行。
什么是文件结尾呢?当Perl读取来自终端的全部输入且你完成Perl数据的输入时,你必须发出通知。为此通常键入一个End of File(文件结束)字符(EOF)。该字符随着你使用的操作系统的不同而各有差别。在UNIX下,该字符通常是在一行的开头使用Ctrl+D;在MS_DOS或者Windows系统上,该字符是在输入的任何位置两次使用Ctrl+Z.
下面的例子用于建立一个100个星号的数组:@a=("*")x100;
$last_pet=qw(cat,dog,fish,iguana);在这个代码段中,赋值运算符右边的那些宠物名字实际上并不是一个列表。它们只是一组字符串直接量,从左到右进行计算,因为表达式的右边是在标量上下文中计算的(因为等号的右边有一个$last_pet)。结果是该$last_pet被设置为‘iguana’.
另一个例子是localtime函数,根据它所在的上下文,可以用两种完全不同的方法来运行。在标量上下文中,localtime函数返回一个格式化很好的当前时间字符串。例如,print scalar(localtime)这个代码,它输出的结果将类似于Thu Sep 16 23:00:06 1999。在列表上下文中,localtime将返回能够描述当前时间的一个元素列表:($sec,$min,$hour,$mday,$mon,$year_off,$wday,$yday,$isdst)=localtime;
foreach语句设置一个索引变量,称为迭代器,它相当于列表的每个元素。请看下面这个例子:foreach $cone (@flavors){ print "I'd like a cone of $cone/n"; }
将标量转换成数组的方法之一是使用split函数。split函数拥有一个模式和一个标量,并且使用该模式来分割该标量。第一个参数是该模式(这里用斜杠括起来),第二个参数是要分割的标量:@words=split(/ /,"The quick brown fox");
若要用数组来创建一个标量,也就是进行split的反向操作,可以使用Perl的join函数。join函数取出一个字符串和一个列表,使用该字符串将列表的各个元素组合在一起,然后返回产生的字符串。请看下面这个例子:$number=join(',', (1..10));
若要给数据排序,Perl提供了sort函数。Sort函数将一个列表作为它的参数,并且大体上按照字母顺序对列表进行排序,然后该函数返回一个排定顺序的新列表。原始数组保持不变。
飞船运算符<=>。如果它左边的操作数小于右边的操作数,那么它返回-1,如果左边的操作数大于右边的操作数,则返回1
当在标量上下文中被赋予一个标量值时,reverse函数能够对字符串的字符进行倒序操作,返回倒序后的字符串。reverse函数能够返回倒序后的列表元素,如下面的例子所示:@lines=qw(I do not like green eggs and ham);print join(' ', reverse @lines);
第5学时 文件操作
若要在Perl中读取文件或写文件,必须打开一个文件句柄.句柄包含了关于如何打开文件和你在文件中读(或写)到了什么位置等信息。它们还包含了用户定义的关于如何读写文件的属性。文件句柄名字的格式与第2学时介绍的变量名基本相同,不同之处是句柄的名字前面没有类型标识符($、@)。因此句柄名字最好使用大写字母.
open的句法如下:open(filehandle, pathname);如果open函数运行成功,它将返回一个非0值;如果open函数运行失败,它返回undef(假).若要关闭文件句柄,可以使用下面这个close函数:close(MYFILE);
如果文件句柄名字重复使用,即另一个文件用相同的文件句柄名字打开,那么原始文件句柄将先被关闭,然后重新打开。
die函数用于停止Perl程序的执行。die和open这两个函数常常以下面的形式同时出现:open(MYFILE, "a.txt") || die;
die函数也可以带有一系列的参数,这些参数将取代默认消息而被输出。如果消息的后面没有换行符,那么消息的结尾就附有at scriptname line xxx字样:die "Can not open."; #终端会显示出错的linedie "Can not open/n"; #终端不会显示出错的line
Perl中有一个特殊的变量$!,它总是设置为系统需要的最后一个操作的出错消息。当$!用于数字上下文时,它返回一个错误号,这个号可能对任何人都没有什么用处;在字符串上下文中,$!返回来自你的操作系统的相应的出错消息:open(MYFILE, "a.txt") || die "Can not open:$!/n";
不过有时并不想使程序停止运行,只是想要发出一个警告。Perl有一个warn函数可供使用。warn的运行方式与die完全一样.
读取文件最常用的方法是使用文件输入运算符,也叫做尖括号运算符(< >)。若要读取文件句柄,只需要将文件句柄放入尖括号运算符中,并将该值赋予一个变量:$line = <MYFILE>;
标量上下文中的尖括号运算符能够读取来自文件的一行输入。当该文件被读完时,尖括号返回值undef。
若要读取和输出整个文件,可以:while(defined($a=<MYFILE>)){ chomp $a; #舍弃行尾字符 print $a;}也可以:while(<MYFILE>){ print $_;}
在列表上下文中,尖括号运算符能够读取整个文件,并将它赋予该列表。文件的每一行被赋予列表或数组的每个元素.在大多数情况下,将整个文件读入一个数组(如果文件不是太大),是处理文件数据的一种非常容易的方法。
打开一个文件以便进行写入操作的句法如下:open (MYFILE, ">pathname");open (MYFILE, ">>pathname");>符号:pathname设定的文件应该用新数据改写,而现有的任何数据都应该删除,同时filehandle是打开的,可以用于写入。>>符号:如果文件存在,那么将数据附加到该文件的结尾处。
当对文件进行写入操作时,操作系统并不将数据存放到磁盘,只是放入缓存;而close函数通知操作系统:数据应该存到磁盘上了。
print函数可以用于将数据写入任何文件句柄,句法如下:print FILEHANDLE list;请注意文件句柄名与列表之间没有逗号
当你将数据写入一个文本文件时,Perl将/n字符序列转换成你的操作系统使用的记录分隔符。在UNIX中,/n变成一个ASCII 10(LF);在Macintosh上,/n转换成ASCII 13(CR);在DOS和Windows系统上,它变成序列ASCII 13和ASCII 10(CRLF)。
当写入二进制数据即GIF文件、EXE文件或MS word文档时,你不希望Perl或操作系统对它进行转换,就必须使用binmode函数,将文件句柄标记为二进制文件。应该在文件句柄打开之后和对它进行输入输出之前使用binmode:open(MYFILE,">>camera.gif") || die "$!";binmode(MYFILE);print MYFILE "GIF87a/056/001/045/015/000";close(MYFILE);
Perl提供了文件测试运算符,句法如下:-x filehandle-x pathname这里的x是指你想进行的特定测试。详见:在perldoc perlfunc,查看“Alphabetical List of Perl Functions”一节的内容。
第6课时 正则表达式
在Perl中,模式被括在模式匹配运算符中间,有时该运算符采用m//的形式,如:m/simon/Perl中的变量$ _常常用于Perl需要默认值的时候。模式匹配是根据$_来进行的,除非你告诉Perl用别的方式来进行匹配。
为了避免在模式中也存在'/',以致引起阅读上的不方便,可以用其他符号代替'/',如:m:simon:但是,若将模式括起来的是'/',那么可以省略m不写,如:/simon/即可。
变量也可以用在正则表达式中。如果在正则表达式中看到一个标量变量, Perl首先计算该标量,然后查看正则表达式。
元字符列表:^ $ ( ) / | @ [ { ? . + *
1 在正则表达式中,圆点用于匹配任何单个字符,除了换行符。2 若要将元字符纳入正则表达式,应该在字符前面加上一个反斜杠'/',使它失去“元”的含义; 但若正常字符前面加反斜杠,则可能反而具备特殊含义,如:/n /t /r /f3 ?用于使前面的字符进行0次或一次匹配(但是不能超过一次)。
如果对一个模式进行0次、一次或多次匹配不能满足你的需要,那么Perl允许根据你需要的具体次数为你进行匹配,方法是使用花括号{}。花括号的格式如下:pattern{n,m}n是匹配的最小次数,m是匹配的最大次数,pattern是试图量化匹配的字符或字符组。可以省略n,也可以省略m,但不能同时省略n和m,如:/x{5,10}/ x至少出现5次,但是不超过10次。/x{9,}/ x至少出现9次,也可能出现更多次。/x{0,4}/ x最多出现4次,也可能根本不出现。/x{8}/ x必须正好出现8次,不能多,也不能少。
Perl的正则表达式拥有一个工具,称为字符类。若要编写一个字符类,可以用方括号[]将这些字符括起来。进行匹配时,字符类中的所有字符被视为单个字符。在一个字符类中,可以设定字符的范围,方法是在上限与下限之间加一个连字符。下面是一些例子:[abcde] 用于匹配a、b、c、d或e中的任何一个字符[a-e] 与上面相同。用于匹配a、b、c、d或e中的任何一个字符G 用于匹配大写字母G或小写字母g[0-9] 用于匹配一个数字[0-9]+ 用于顺序匹配一个或多个数字[A-Za -z]{5} 用于匹配任何一组5个字母字符[*!@#$%&()] 用于匹配这些符号中的任何一个注意:最后一个例子表明,在字符类中,大多数通配符会失去它们的“通配符性质”,即:它们的运行特性将类似其他任何一个普通字符。
如果'^'作为字符类中的第一个字符,该字符类将变为无效,如:/[^A-Z]/;
Perl包含了某些常用字符类的快捷方式。它们用反斜杠和通配符来表示,如表6-2所示。下面是一些例子://d{5}/; # matches 5 digits//s/w+/s/; # matches a group of word-characters surrounded by space
表6-2 特殊字符类:/w 一个单词字符,与[a-zA -z0 -9_ ]相同/W 一个非单词字符(与/w相反)/d 一个数字,与[0-9]相同/D 一个非数字/s 一个白空间字符,与[/t/f/r/n]相同/S 一个非白空间字符
在列表上下文中,匹配运算符返回括号中匹配的表达式的各个部分的一个列表。每个加括号的值都是列表的返回值,如果模式不包含括号,则返回1:$_="apple is red";($fruit,$color)=/(.*)/sis/s(.*)/;
两个位置通配符:第一个位置通配符是'^'。正则表达式开头的'^'告诉正则表达式只匹配一行开头的字符。如/^video/只匹配单词video,若它位于一行开头。与它相对应的通配符是'$'。正则表达式结尾处的'$'能够使模式只匹配一行结尾的字符。如/earth$ /用于匹配earth ,不过它只能位于行尾。如:/^Help/ 只匹配以Help开头的行/^Frankly.*darn$/ 只用于匹配以Frankly开头和以darn结尾的行。它们中间的所有字符也进行匹配/^hysteria$/ 只用于匹配只包含单词hysteria的行/^$/ 只用于匹配一行的开头,紧接着匹配该行的结尾。它只用于匹配空行/^/ 只用于匹配带有开头字符的行(所有行)。/$/的作用也相同
替换:为了达到修改数据的目的,方法之一是使用替换运算符s///,句法如下:s/serchpattern/replacement/;替换运算符也可以使用非斜杠(/)的界限符,使用的方法与匹配运算符相同,如:s#street#avenue#;
如果匹配项的后面跟一个字母i,替换运算符(s///)和匹配运算符(m//)能够在匹配正则表达式时不考虑大小写字母。/macbeth/i;
以上所述一切都是针对$_进行匹配而言的。如果要对非$_的变量使用匹配运算符和替换运算符,则必须将它们与该变量连接起来,为此可以使用连接运算符=~=~运算符并不进行赋值,它只是取出右边的运算符,并使它对左边的变量进行操作,如:$poem="one fish, two fish, three fish";$flag=$poem=~/fish/; #若$poem中有fish,则$flag为真。$poem=~s/fish/cat/; #只有一个fish被替换成cat$poem=~s/fish/cat/g; #加上全局匹配修饰符g之后,所有的fish都被替换成cat
在列表上下文中,全局匹配修饰符g可使匹配代码返回一个放在括号中的正则表达式的各个部分的列表:$_="One fish, two frog, red fred, blue foul";@F=//W(f/w/w/w)/g;该表达式被计算后,变量@F将包含4个元素,即fish、frog、fred和foul
当将括号用于Perl的正则表达式中时,由每个带括号的表达式进行匹配的目标字符串的这个部分将被记住。Perl将把这个匹配的文本记录在一些特殊的变量中,这些变量的名字是$1(用于第一组括号)、$2(用于第二组括号)、$3、和$4等等,如:$phone=800-812-8956;print "The area code is $1./n" if($phone=~/(/d{3})-(/d{3})-(/d{4}));
一个新函数grep,句法如下:grep expression, list;grep block list;例如,将一个文件读入一个数组,grep函数迭代运行通过list中的每个元素,然后执行expression或block。在expression或block中,$_被设置为要计算的列表中的每个元素。如果该表达式返回真,grep就返回该元素。
这里你必须记住两点:1 在表达式中, $_是对列表中的实际值的引用。如果修改$_,就会改变列表中的原始元素:@hounds=greps/hound/hounds/,@dogs;2 grep不一定必须与模式匹配或替换运算符一道使用,它可以与任何运算符一道使用。下面这个例子用于检索长度超过8个字符的犬名:@longdogs=grep length($_)>8, @dogs;
一个相关函数map的句法与grep基本相同,不过它的表达式(或语句块)返回的值是从map返回的,而不是$_的值。可以使用map函数,根据第一个数组来产生第二个数组,如:@ words= map {split ' ',$_} @input;在这个例子中,数组@input的每个元素(作为$_传递给语句块)均用空格隔开。这意味着@input的每个元素均产生一个单词列表。该列表存放在@words中。@input的每个相邻行均被分隔开来,并在@words中进行累加。
第7学时 哈希结构
数组与哈希的差别是:哈希是按照名字来访问它们的标量的,而不是像数组那样使用数字标号进行访问。
在Perl中,哈希变量是以百分比符号(%)来标识的,它们与数组和标量不使用相同的名字。例如,你可以拥有一个名字叫%a的哈希变量,也可以有一个名字叫@a的数组.
若要创建哈希元素,只需要将值赋予这些元素即可,如:$Authors{'Dune'}='Frank Herbert';或:$Authors{Dune}='Frank Herbert';在这个例子中,将%Authors赋予哈希结构。该元素的关键字是单词Dune,数据是名字Frank Herbert 。
$Authors{'Dune'}可以像任何其他标量那样来进行处理,它可以被传递给函数,被操作员修改,可以输出,也可以重新赋值。当修改一个哈希元素时,请始终记住,你是修改存放在哈希元素中的值,不是修改哈希本身的值。
$food{'apple'}='fruit';$food{'pear'}='fruit';$food{'carrot'}='vegetable';以上三句也可简写成:%food=('apple','fruit','pear','fruit','carrot','vegetable');更好的写法是:%food=('apple'=>'fruit','pear'=>'fruit','carrot'=>'vegetable');或%food=(apple=>'fruit',pear=>'fruit',carrot=>'vegetable');
keys函数:该函数的参数是一个哈希结构,返回值是一个列表,该列表包含了该哈希结构的所有关键字,如:foreach $film (keys %Movies){ print "$film/n"; }
values函数:该函数的参数是一个哈希结构,返回值是一个列表,该列表包含了该哈希结构的所有值,其顺序与keys函数的返回值的顺序相对应。
有时需要按值从哈希结构中检索各个元素,最好方法是对哈希结构进行反转,也就是说,所有关键字变成值,所有值变成新哈希结构的关键字。使用reverse函数对哈希结构进行键值反转:%ByDirectors = reverse %Movies;注意:如果原来的哈希结构拥有重复的值,那么得到的新的哈希结构拥有的元素将比原先要少。
每当哈希结构用于列表环境中时,Perl会将哈希结构重新变为由关键字和值组成的普通列表。若要拷贝一个哈希结构,只需赋予即可:%New_Hash = %Old_Hash;%Both = (%First, %Second);如果%First的有些关键字也出现在%Second中,那么第二次出现的关键字值对就取代%Both中的第一个关键字值对。
exists函数:用于测试哈希结构中是否存在哈希关键字.如果存在,返回真;否则返回假:if(exists %Hash{keyvalue}){ ......}
delete函数:用于从哈希结构删除关键字,如:$delete $Hash{keyvalue};若要删除一个哈希结构的所有关键字和值,只需要将其设置为一个空列表:%Hash=();
第8学时 函数
在Perl中,用户定义的函数称为子例程,如:sub func(){...}调用的方法:&func(); 或 func();
子例程的返回值是子例程中计算的最后一个表达式的值,或者是return语句显式返回的值。
在子例程中,被传递的参数可以通过Perl的特殊变量@_来访问,如:sub printargs{ print join(',', @_);}printargs('market', 'home', 'beef');
若要像下面这个例子中那样,访问传递过来的各个参数,可以使用数组@_上的下标。$_[0](@_的一个下标)与标量变量$_毫不相干:sub print_third_argument{ print $_[2];}
变量@_实际上包含了传递给子例程的原始参数的别名。如果修改了@_(或者修改了@_的任何元素),就会修改参数列表中的元素变量。
要使变量成为函数的专用变量,必须使用my操作符:sub moonweight{ my $weight; ($weight = @_); return $weght/6;}
Perl的5.004和更新的版本允许将for和foreach循环中的迭代器以及while和if中的测试条件声明为代码块的专用迭代器和测试条件:foreach my $var (@array){ print "$var/n"; }
用local声明的变量的作用与使用my声明的变量几乎相同,差别是:声明为局部变量的那些变量,可以在它的作用域范围内的代码块中看到,也可以在从该代码块中调用的任何子例程中看到。