创意安天

 找回密码
 注册创意安天

[精华] Perl教学

[复制链接]
发表于 2009-7-23 13:46 | 显示全部楼层 |阅读模式
swordlea

版主

发贴: 652
积分: 0
来自: 论坛神游夜
注册日期: 2006-05-16
  发表时间: 2006-09-22 16:01:50  

--------------------------------------------------------------------------------
第一章 概述
一、Perl是什么?
Perl是Practical Extraction and Report Language的缩写,它是由Larry Wall设计的,并由他不断更新和维护,用于在UNIX环境下编程。
.Perl具有高级语言(如C)的强大能力和灵活性。事实上,你将看到,它的许多特性是从C语言中借用来的。
.与脚本语言一样,Perl不需要编译器和链接器来运行代码,你要做的只是写出程序并告诉Perl来运行而已。这意味着Perl对于小的编程问题的快速解决方案和为大型事件创建原型来测试潜在的解决方案是十分理想的。
.Perl提供脚本语言(如sed和awk)的所有功能,还具有它们所不具备的很多功能。Perl还支持sed到Perl及awd到Perl的翻译器。
简而言之,Perl象C一样强大,象awk、sed等脚本描述语言一样方便。
二、Perl在哪里?
Perl通常位于/usr/local/bin/perl或/usr/bin/perl中。你可以在Internet用匿名FTP免费得到它,如ftp://prep.ai.mit.edu/pub/gnu/perl-5.004.tar.gz。
安装过程为:
(1)解压:
$gunzip perl-5.004.tar.gz
$tar xvf perl-5.004.tar.gz
(2)配置:
$Configure
(3)编译:
$make
$make test
$make install
(3)放置:
将编译生成的可执行文件拷贝到可执行文件通常所在目录,如:
$copy <compiled excutable file> /usr/local/bin/perl
注:这需要系统管理员权限。
北美地址 目录
ftp.netlabs.com IP地址:192.94.48.152
目录 /pub/outgoing/perl5.0
ftp.cis.ufl.edu IP地址: 128.227.100.198
目录 /pub/perl/src/5.0
ftp.uu.net IP地址: 192.48.96.9
目录 /languages/perl
ftp.khoros.unm.edu IP地址: 198.59.155.28
目录 /pub/perl
ftp.cbi.tamucc.edu IP地址: 165.95.1.3
目录 /pub/duff/Perl
ftp.metronet.com IP地址: 192.245.137.1
目录 /pub/perl/sources
genetics.upenn.edu IP地址: 128.91.200.37
目录 /perl5
欧洲Site Location
ftp.cs.ruu.nl IP地址: 131.211.80.17
目录 /pub/PERL/perl5.0/src
ftp.funet.fi IP地址: 128.214.248.6
目录 /pub/languages/perl/ports/perl5
ftp.zrz.tu-berlin.de IP地址: 130.149.4.40
目录 /pub/unix/perl
src.doc.ic.ac.uk IP地址: 146.169.17.5
目录 /packages/perl5
澳洲Site Location
sungear.mame.mu.oz.au IP地址: 128.250.209.2
目录 /pub/perl/src/5.0
南美Site Location
ftp.inf.utfsm.cl IP地址: 146.83.198.3
目录 /pub/gnu
三、运行
用文本编辑器编辑好你的Perl程序,加上可执行属性:$chmod +x <program>就可以执行了:$./<program>。如果系统提示:"/usr/local/bin/perl not found",则说明你没有安装成功,请重新安装。
注:你的程序的第一行必须为#!/usr/local/bin/perl(perl所在位置)。
四、注释:
注释的方法为在语句的开头用字符#,如:
# this line is a comment
注:建议经常使用注释使你的程序易读,这是好的编程习惯。
第二章 简单变量
一、整型
二、浮点数
三、字符串
基本上,简单变量就是一个数据单元,这个单元可以是数字或字符串。
一、整型
1、整型
PERL最常用的简单变量,由于其与其它语言基本相同,不再赘述。
例:
$x = 12345;
if (1217 + 116 == 1333) {
# statement block goes here
}
整型的限制:
PERL实际上把整数存在你的计算机中的浮点寄存器中,所以实际上被当作浮点数看待。在多数计算机中,浮点寄存器可以存贮约16位数字,长于此的被丢弃。整数实为浮点数的特例。
2、8进制和16进制数
8进制以0打头,16进制以0x打头。
例:$var1 = 047; (等于十进制的39)
$var2 = 0x1f; (等于十进制的31)
二、浮点数
如 11.4 、 -0.3 、.3 、 3. 、 54.1e+02 、 5.41e03
浮点寄存器通常不能精确地存贮浮点数,从而产生误差,在运算和比较中要特别注意。指数的范围通常为-309到+308。
例:
#!/usr/local/bin/perl
$value = 9.01e+21 + 0.01 - 9.01e+21;
print ("first value is ", $value, "\n";
$value = 9.01e+21 - 9.01e+21 + 0.01;
print ("second value is ", $value, "\n";
---------------------------------------------------------
$ program3_3
first value is 0
second value is 0.01
三、字符串
惯用C的程序员要注意,在PERL中,字符串的末尾并不含有隐含的NULL字符,NULL字符可以出现在串的任何位置。
. 双引号内的字符串中支持简单变量替换,例如:
$number = 11;
$text = "This text contains the number $number.";
则$text的内容为:"This text contains the number 11."
perl 替换时,会尽可能寻找最长的变量名,可能会造成麻烦,可以在变量名前后有大括号括起来,如
$num=2;
$text = "This text contains the number ${num}ber.";
#text is now "This text contains the number 2ber."
.双引号内的字符串中支持转义字符
Table 3.1. Escape sequences in strings.Escape Sequence Description
\a Bell (beep)
\b Backspace
\cn The Ctrl+n character
\e Escape
\E Ends the effect of \L, \U or \Q
\f Form feed
\l Forces the next letter into lowercase
\L All following letters are lowercase
\n Newline
\r Carriage return
\Q Do not look for special pattern characters
\t Tab
\u Force next letter into uppercase
\U All following letters are uppercase
\v Vertical tab
\L、\U、\Q功能可以由\E关闭掉,如:
$a = "T\LHIS IS A \ESTRING"; # same as "This is a STRING"
.要在字符串中包含双引号或反斜线,则在其前加一个反斜线,反斜线还可以取消变量替换,如:
$res = "A quote \" and A backslash \\";
$result = 14;
print ("The value of \$result is $result.\n"的结果为:
The value of $result is 14.
.可用\nnn(8进制)或\xnn(16进制)来表示ASCII字符,如:
$result = "\377"; # this is the character 255,or EOF
$result = "\xff"; # this is also 255
.单引号字符串
单引号字符串与双引号字符串有两个区别,一是没有变量替换功能,二是反斜线不支持转义字符,而只在包含单引号和反斜线时起作用。单引号另一个特性是可以跨多行,如:
$text = 'This is two
lines of text
';
与下句等效:
$text = "This is two\nlines of text\n";
.字符串和数值的互相转换
例1:
$string = "43";
$number = 28;
$result = $string + $number; # $result = 71
若字符串中含有非数字的字符,则从左起至第一个非数字的字符,如:
$result = "hello" * 5; # $result = 0
$result = "12a34" +1; # $result = 13
.变量初始值
在PERL中,所有的简单变量都有缺省初始值:"",即空字符。但是建议给所有变量赋初值,否则当程序变得大而复杂后,很容易出现不可预料且很难调试的错误。
第三章 操作符
一、算术操作符
二、整数比较操作符
三、字符串比较操作符
四、逻辑操作符
五、位操作符
六、赋值操作符
七、自增自减操作符
八、字符串联结和重复操作符
九、逗号操作符
十、条件操作符
十一、操作符的次序
一、算术操作符 :+(加)、-(减)、*(乘)、/(除)、**(乘幂)、%(取余)、-(单目负)
(1)乘幂的基数不能为负,如 (-5) ** 2.5 # error;
(2)乘幂结果不能超出计算机表示的限制,如10 ** 999999 # error
(3)取余的操作数如不是整数,四舍五入成整数后运算;运算符右侧不能为零
(4)单目负可用于变量: - $y ; # 等效于 $y * -1
二、整数比较操作符
Table 3.1. 整数比较操作符操作符 描述
< 小于
> 大于
== 等于
<= 小于等于
>= 大于等于
!= 不等于
<=> 比较,返回 1, 0, or -1
操作符<=>结果为:
0 - 两个值相等
1 - 第一个值大
1 - 第二个值大
三、字符串比较操作符
Table 3.2. 字符串比较操作符操作符 描述  
lt 小于  
gt 大于  
eq 等于  
le 小于等于  
ge 大于等于  
ne 不等于  
cmp 比较,返回 1, 0, or -1  
四、逻辑操作符
逻辑或:$a || $b 或 $a or $b
逻辑与:$a && $b 或 $a and $b
逻辑非:! $a 或 not $a
逻辑异或:$a xor $b
五、位操作符
位与:&
位或:|
位非:~
位异或:^
左移:$x << 1
右移:$x >> 2
注:不要将&用于负整数,因为PERL将会把它们转化为无符号数。
六、赋值操作符
Table 3.3. 赋值操作符操作符 描述
= Assignment only
+= Addition and assignment
-= Subtraction and assignment
*= Multiplication and assignment
/= Division and assignment
%= Remainder and assignment
**= Exponentiation and assignment
&= Bitwise AND and assignment
|= Bitwise OR and assignment
^= Bitwise XOR and assignment
Table 3.4. 赋值操作符例子表达式 等效表达式
$a = 1; none (basic assignment)
$a -= 1; $a = $a - 1;
$a *= 2; $a = $a * 2;
$a /= 2; $a = $a / 2;
$a %= 2; $a = $a % 2;
$a **= 2; $a = $a ** 2;
$a &= 2; $a = $a & 2;
$a |= 2; $a = $a | 2;
$a ^= 2; $a = $a ^ 2;
.=可在一个赋值语句中出现多次,如:
$value1 = $value2 = "a string";
.=作为子表达式
($a = $ += 3;
等价于
$a = $b;
$a += 3;
但建议不要使用这种方式。
七、自增自减操作符 :++、--(与C++中的用法相同)
.不要在变量两边都使用此种操作符:++$var-- # error
.不要在变量自增/减后在同一表达式中再次使用:$var2 = $var1 + ++$var1; # error
.在PERL中++可用于字符串,但当结尾字符为'z'、'Z'、'9'时进位,如:
$stringvar = "abc";
$stringvar++; # $stringvar contains "abd" now
$stringvar = "aBC";
$stringvar++; # $stringvar contains "aBD" now
$stringvar = "abz";
$stringvar++; # $stringvar now contains "aca"
$stringvar = "AGZZZ";
$stringvar++; # $stringvar now contains "AHAAA"
$stringvar = "ab4";
$stringvar++; # $stringvar now contains "ab5"
$stringvar = "bc999";
$stringvar++; # $stringvar now contains "bd000"
.不要使用--,PERL将先将字符串转换为数字再进行自减
$stringvar = "abc";
$stringvar--; # $stringvar = -1 now
.如果字符串中含有非字母且非数字的字符,或数字位于字母中,则经过++运算前值转换为数字零,因此结果为1,如:
$stringvar = "ab*c";
$stringvar++;
$stringvar = "ab5c";
$stringvar++;
八、字符串联结和重复操作符
联接: .
重复:x
联接且赋值(类似+=): .=
例:
$newstring = "potato" . "head";
$newstring = "t" x 5;
$a = "be";
$a .= "witched"; # $a is now "bewitched"
九、逗号操作符
其前面的表达式先进行运算,如:
$var1 += 1, $var2 = $var1;
等价于
$var1 += 1;
$var2 = $var1;
使用此操作符的唯一理由是提高程序的可读性,将关系密切的两个表达式结合在一起,如:
$val = 26;
$result = (++$val, $val + 5); # $result = 32
注意如果此处没有括号则意义不同:
$val = 26;
$result = ++$val, $val + 5; # $result = 27
十、条件操作符
与C中类似,条件?值1:值2,当条件为真时取值1,为假时取值2,如:
$result = $var == 0 ? 14 : 7;
$result = 43 + ($divisor == 0 ? 0 : $dividend / $divisor);
PERL 5中,还可以在赋值式左边使用条件操作符来选择被赋值的变量,如:
$condvar == 43 ? $var1 : $var2 = 14;
$condvar == 43 ? $var1 = 14 : $var2 = 14;
十一、操作符的次序
Table 3.6. 操作符次序操作符 描述
++, -- 自增,自减
-, ~, ! 单目
** 乘方
=~, !~ 模式匹配
*, /, %, x 乘,除,取余,重复
+, -, . 加,减,联接
<<, >> 移位
-e, -r, etc. 文件状态
<, <=, >, >=, lt, le, gt, ge 不等比较
==, !=, <=>, eq, ne, cmp 相等比较
& 位与
|, ^ 位或,位异或
&& 逻辑与
|| 逻辑或
.. 列表范围
? and : 条件操作符
=, +=, -=, *=, 赋值
and so on  
, 逗号操作符
not Low-precedence logical NOT
and Low-precedence logical AND
or, xor Low-precedence logical OR and XOR
.操作符结合性(associativity):
Table 3.7. 操作符结合性操作符 结合性
++, -- 无
-, ~, ! Right-to-left
** Right-to-left
=~, !~ Left-to-right
*, /, %, x Left-to-right
+, -, . Left-to-right
<<, >> Left-to-right
-e, -r, 无
<, <=, >, >=, lt, le, gt, ge Left-to-right
==, !=, <=>, eq, ne, cmp Left-to-right
& Left-to-right
|, ^ Left-to-right
&& Left-to-right
|| Left-to-right
.. Left-to-right
? and : Right-to-left
=, +=, -=, *=, Right-to-left
and so on  
, Left-to-right
not Left-to-right
and Left-to-right
or, xor Left-to-right
建议:
1、当你不确定某操作符是否先执行时,一定要用括号明确之。
2、用多行、空格等方式提高程序的可读性。
第四章 列表和数组变量
一、列表
二、数组--列表的存贮
1、数组的存取
2、字符串中的方括号和变量替换
3、列表范围
4、数组的输出
5、列表/数组的长度
6、子数组
7、有关数组的库函数
一、列表
列表是包含在括号里的一序列的值,可以为任何数值,也可为空,如:(1, 5.3 , "hello" , 2),空列表:()。
注:只含有一个数值的列表(如:(43.2) )与该数值本身(即:43.2 )是不同的,但它们可以互相转化或赋值。
列表例:
(17, $var, "a string"
(17, 26 << 2)
(17, $var1 + $var2)
($value, "The answer is $value"
二、数组--列表的存贮
列表存贮于数组变量中,与简单变量不同,数组变量以字符"@"打头,如:
@array = (1, 2, 3);
注:
(1)数组变量创建时初始值为空列表:()。
(2)因为PERL用@和$来区分数组变量和简单变量,所以同一个名字可以同时用于数组变量和简单变量,如:
$var = 1;
@var = (11, 27.1 , "a string";
但这样很容易混淆,故不推荐。
1、数组的存取
.对数组中的值通过下标存取,第一个元素下标为0。试图访问不存在的数组元素,则结果为NULL,但如果给超出数组大小的元素赋值,则数组自动增长,原来没有的元素值为NULL。如:
@array = (1, 2, 3, 4);
$scalar = $array[0];
$array[3] = 5; # now @array is (1,2,3,5)
$scalar = $array[4]; # now $scalar = null;
$array[6] = 17; # now @array is (1,2,3,5,"","",17)
.数组间拷贝
@result = @original;
.用数组给列表赋值
@list1 = (2, 3, 4);
@list2 = (1, @list1, 5); # @list2 = (1, 2, 3, 4, 5)
.数组对简单变量的赋值
(1) @array = (5, 7, 11);
($var1, $var2) = @array; # $var1 = 5, $var2 = 7, 11被忽略
(2) @array = (5, 7);
($var1, $var2, $var3) = @array; # $var1 = 5, $var2 = 7, $var3 ="" (null)
.从标准输入(STDIN)给变量赋值
$var = <STDIN>;
@array = <STDIN>; # ^D为结束输入的符号
2 、字符串中的方括号和变量替换
"$var[0]" 为数组@var的第一个元素。
"$var\[0]" 将字符"["转义,等价于"$var". "[0]",$var被变量替换,[0]保持不变。
"${var}[0]" 亦等价于"$var" ."[0]"。
"$\{var}"则取消了大括号的变量替换功能,包含文字:${var}.
3、列表范围:
(1..10) = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
(2, 5..7, 11) = (2, 5, 6, 7, 11)
(3..3) = (3)
.用于实数
(2.1..5.3) = (2.1, 3.1 ,4.1, 5.1)
(4.5..1.6) = ()
.用于字符串
("aaa".."aad" = ("aaa","aab", "aac", "aad"
@day_of_month = ("01".."31"
.可包含变量或表达式
($var1..$var2+5)
.小技巧:
$fred = "Fred";
print (("Hello, " . $fred . "!\n" x 2);
其结果为:
Hello, Fred!
Hello, Fred!
4、数组的输出:
(1) @array = (1, 2, 3);
print (@array, "\n";
结果为:
123
(2) @array = (1, 2, 3);
print ("@array\n";
结果为:
1 2 3
5、列表/数组的长度
当数组变量出现在预期简单变量出现的地方,则PERL解释器取其长度。
@array = (1, 2, 3);
$scalar = @array; # $scalar = 3,即@array的长度
($scalar) = @array; # $scalar = 1,即@array第一个元素的值
注:以数组的长度为循环次数可如下编程:
$count = 1;
while ($count <= @array) {
print ("element $count: $array[$count-1]\n";
$count++;
}
6、子数组
@array = (1, 2, 3, 4, 5);
@subarray = @array[0,1]; # @subarray = (1, 2)
@subarray2 = @array[1..3]; # @subarray2 = (2,3,4)
@array[0,1] = ("string", 46); # @array =("string",46,3,4,5) now
@array[0..3] = (11, 22, 33, 44); # @array = (11,22,33,44,5) now
@array[1,2,3] = @array[3,2,4]; # @array = (11,44,33,5,5) now
@array[0..2] = @array[3,4]; # @array = (5,5,"",5,5) now
可以用子数组形式来交换元素:
@array[1,2] = @array[2,1];
7、有关数组的库函数
(1)sort--按字符顺序排序
@array = ("this", "is", "a","test";
@array2 = sort(@array); # @array2 = ("a","is", "test", "this"
@array = (70, 100, 8);
@array = sort(@array); # @array = (100, 70, 8) now
内置的sort函数是以ASCII码顺序为列表排序,事实上,perl提供的sort用途非常广泛,可用任何自定义的方式对数据排序。
为了加快执行速度,我们用$a、$b这两个全局变量($a、$b原来的值会被存起来,不必担心)进行比较。如果$a小于$b、应返回任何负整数;相等返回0;大干返回正整数。注意、这里所谓的“小于”是依据函数的定义,可能是数值比较,或是看字符串的第二个字符。规则完全没有限制。
下面的函数会进行数值比较:
sub by_number {
if($a<$ {
return 1;
}elsif($a==$ {
return 0;
}elsif($a>$ {
return -1;
}
}
试看排序以下列表:
@somelist=(1,2,4,8,16,32,64,128,256);
如果用原来的 sort函数排序,会得到按ASCII码顺序当作字符串的排序结果:
@wronglist = sort @somelist;
#@wronglist is now (1,128,16,2,256,32,4,64,8)
这当然不是数字顺序。好吧,我们给sort自定义排序函数,自定义函数的名称必须紧跟sort关键字,像这样:
@rightlist= sort by_number @somelist;
@rightlist is now (1,2,4,8,16,32,64,128,256)
看吧,这样念起来就是“sort by number”,多自然啊:这就为什么建议排序函数用“by_”字样开头的原因。
( 2)reverse--反转数组
@array2 = reverse(@array);
@array2 = reverse sort (@array);
(3)chop--数组去尾 ----->see also chomp
chop的意义是去掉STDIN(键盘)输入字符串时最后一个字符--换行符。而如果它作用到数组上,则将数组中每一个元素都做如此处理。
@list = ("rabbit", "12345","quartz";
chop (@list); # @list = ("rabbi", "1234","quart" now
( 4)join/split--连接/拆分
join的第一个参数是连接所用的中间字符,其余则为待连接的字符数组。
$string = join(" ", "this", "is","a", "string"; # 结果为"this is a string"
@list = ("words","and";
$string = join("::", @list, "colons"; #结果为"words::and::colons"
@array = split(/::/,$string); # @array = ("words","and", "colons" now
第五章 文件读写
一、打开、关闭文件
二、读文件
三、写文件
四、判断文件状态
五、命令行参数
六、打开管道
一、打开、关闭文件
语法为open (filevar, filename),其中filevar为文件句柄,或者说是程序中用来代表某文件的代号,filename为文件名,其路径可为相对路径,亦可为绝对路径。
open(FILE1,"file1";
open(FILE1, "/u/jqpublic/file1";
打开文件时必须决定访问模式,在PERL中有三种访问模式:读、写和添加。后两种模式的区别在于写模式将原文件覆盖,原有内容丢失,形式为:open(outfile,">outfile";而添加模式则在原文件的末尾处继续添加内容,形式为:open(appendfile, ">>appendfile"。要注意的是,不能对文件同时进行读和写/添加操作。
open的返回值用来确定打开文件的操作是否成功,当其成功时返回非零值,失败时返回零,因此可以如下判断:
if (open(MYFILE, "myfile") {
# here's what to do if the file opened successfully
}
当文件打开失败时结束程序:
unless (open (MYFILE, "file1") {
die ("cannot open input file file1\n";
}
亦可用逻辑或操作符表示如下:
open (MYFILE, "file1" || die ("Could not open file";
当文件操作完毕后,用close(MYFILE); 关闭文件。
二、读文件
语句$line = <MYFILE>;从文件中读取一行数据存储到简单变量$line中并把文件指针向后移动一行。<STDIN>为标准输入文件,通常为键盘输入,不需要打开。
语句@array = <MYFILE>;把文件的全部内容读入数组@array,文件的每一行(含回车符)为@array的一个元素。
三、写文件
形式为:
open(OUTFILE, ">outfile";
print OUTFILE ("Here is an output line.\n";
注:STDOUT、STDERR为标准输出和标准错误文件,通常为屏幕,且不需要打开。
四、判断文件状态
1、文件测试操作符
语法为:-op expr,如:
if (-e "/path/file1" {
print STDERR ("File file1 exists.\n";
}
文件测试操作符操作符 描述
-b 是否为块设备
-c 是否为字符设备
-d 是否为目录
-e 是否存在
-f 是否为普通文件
-g 是否设置了setgid位
-k 是否设置了sticky位
-l 是否为符号链接
-o 是否拥有该文件
-p 是否为管道
-r 是否可读
-s 是否非空
-t 是否表示终端
-u 是否设置了setuid位
-w 是否可写
-x 是否可执行
-z 是否为空文件
-A 距上次访问多长时间
-B 是否为二进制文件
-C 距上次访问文件的inode多长时间
-M 距上次修改多长时间
-O 是否只为“真正的用户”所拥有
-R 是否只有“真正的用户”可读
-S 是否为socket
-T 是否为文本文件
-W 是否只有"真正的用户"可写
-X 是否只有"真正的用户"可执行
注:“真正的用户”指登录时指定的userid,与当前进程用户ID相对,命令suid可以改变有效用户ID。
例:
unless (open(INFILE, "infile") {
die ("Input file infile cannot be opened.\n";
}
if (-e "outfile" {
die ("Output file outfile already exists.\n";
}
unless (open(OUTFILE, ">outfile") {
die ("Output file outfile cannot be opened.\n";
}
等价于
open(INFILE, "infile" && !(-e "outfile" &&
open(OUTFILE, ">outfile" || die("Cannot open files\n";
五、命令行参数
象C一样,PERL也有存储命令行参数的数组@ARGV,可以用来分别处理各个命令行参数;与C不同的是,$ARGV[0]是第一个参数,而不是程序名本身。
$var = $ARGV[0]; # 第一个参数
$numargs = @ARGV; # 参数的个数
PERL中,<>操作符实际上是对数组@ARGV的隐含的引用,其工作原理为:
1、当PERL解释器第一次看到<>时,打开以$ARGV[0]为文件名的文件;
2、执行动作shift(@ARGV); 即把数组@ARGV的元素向前移动一个,其元素数量即减少了一个。
3、<>操作符读取在第一步打开的文件中的所有行。
4、读完后,解释器回到第一步重复。
例:
@ARGV = ("myfile1", "myfile2"; #实际上由命令行参数赋值
while ($line = <> {
print ($line);
}
将把文件myfile1和myfile2的内容打印出来。
六、打开管道
用程序的形式也可以象命令行一样打开和使用管道(ex:ls > tempfile)。如语句open (MYPIPE, "| cat >hello"; 打开一个管道,发送到MYPIPE的输出成为命令"cat >hello"的输入。由于cat命令将显示输入文件的内容,故该语句等价于open(MYPIPE, ">hello"; 用管道发送邮件如下:
open (MESSAGE, "| mail dave";
print MESSAGE ("Hi, Dave! Your Perl program sent this!\n";
close (MESSAGE);
第六章 模式匹配
一、简介
二、匹配操作符
三、模式中的特殊字符
1、字符+
2、字符 []和[^]
3、字符 *和?
4、转义字符
5、匹配任意字母或数字
6、锚模式
7、模式中的变量替换
8、字符范围转义前缀
9、匹配任意字符
10、匹配指定数目的字符
11、指定选项
12、模式的部分重用
13、转义和特定字符的执行次序
14、指定模式定界符
15、模式次序变量
四、模式匹配选项
1、匹配所有可能的模式(g选项)
2、忽略大小写(i选项)例
3、将字符串看作多行(m选项)
4、只执行一次变量替换例
5、将字符串看作单行例
6、在模式中忽略空格
五、替换操作符
六、翻译操作符
七、扩展模式匹配
1、不存贮括号内的匹配内容
2、内嵌模式选项
3、肯定的和否定的预见匹配
4、模式注释
一、简介
模式指在字符串中寻找的特定序列的字符,由反斜线包含:/def/即模式def。其用法如结合函数split将字符串用某模式分成多个单词:@array = split(/ /, $line);
二、匹配操作符 =~、!~
=~检验匹配是否成功:$result = $var =~ /abc/;若在该字符串中找到了该模式,则返回非零值,即true,不匹配则返回0,即false。!~则相反。
这两个操作符适于条件控制中,如:
if ($question =~ /please/) {
print ("Thank you for being polite!\n";
}
else {
print ("That was not very polite!\n";
}
三、模式中的特殊字符
PERL在模式中支持一些特殊字符,可以起到一些特殊的作用。
1、字符 +
+意味着一个或多个相同的字符,如:/de+f/指def、deef、deeeeef等。它尽量匹配尽可能多的相同字符,如/ab+/在字符串abbc中匹配的将是abb,而不是ab。
当一行中各单词间的空格多于一个时,可以如下分割:
@array = split (/ +/, $line);
注:split函数每次遇到分割模式,总是开始一个新单词,因此若$line以空格打头,则@array的第一个元素即为空元素。但其可以区分是否真有单词,如若$line中只有空格,则@array则为空数组。且上例中TAB字符被当作一个单词。注意修正。
2、字符 []和[^]
[]意味着匹配一组字符中的一个,如/a[0123456789]c/将匹配a加数字加c的字符串。与+联合使用例:/d[eE]+f/匹配def、dEf、deef、dEdf、dEEEeeeEef等。^表示除其之外的所有字符,如:/d[^deE]f/匹配d加非e字符加f的字符串。
3、字符 *和?
它们与+类似,区别在于*匹配0个、1个或多个相同字符,?匹配0个或1个该字符。如/de*f/匹配df、def、deeeef等;/de?f/匹配df或def。
注意:* + ? 都是“greedy”的
若表达式中出现两个重复符号,perl遵守“最左边最贪心”(leftmost is greediest)的规则,例:
$_="a xxx c xxxx c xxxx d";
/a.*c.*d/;
".*"会和第二个c之前的所有字符符合。
再重复符号后加个问号,可以让它变得不贪心:
/a.*?c.*d/;
"a.*?c"会和最少的a,c之间字符匹配。
4、转义字符
如果你想在模式中包含通常被看作特殊意义的字符,须在其前加斜线"\"。如:/\*+/中\*即表示字符*,而不是上面提到的一个或多个字符的含义。斜线的表示为/\\/。在PERL5中可用字符对\Q和\E来转义。
5、匹配任意字母或数字
上面提到模式/a[0123456789]c/匹配字母a加任意数字加c的字符串,另一种表示方法为:/a[0-9]c/,类似的,[a-z]表示任意小写字母,[A-Z]表示任意大写字母。任意大小写字母、数字的表示方法为:/[0-9a-zA-Z]/。
6、锚模式
锚 描述
^ 或 \A 仅匹配串首
$ 或 \Z 仅匹配串尾
\b 匹配单词边界
\B 单词内部匹配
例1:/^def/只匹配以def打头的字符串,/$def/只匹配以def结尾的字符串,结合起来的/^def$/只匹配字符串def(?)。\A和\Z在多行匹配时与^和$不同。
例2:检验变量名的类型:
if ($varname =~ /^\$[A-Za-z][_0-9a-zA-Z]*$/) {
print ("$varname is a legal scalar variable\n";
} elsif ($varname =~ /^@[A-Za-z][_0-9a-zA-Z]*$/) {
print ("$varname is a legal array variable\n";
} elsif ($varname =~ /^[A-Za-z][_0-9a-zA-Z]*$/) {
print ("$varname is a legal file variable\n";
} else {
print ("I don't understand what $varname is.\n";
}
例3:\b在单词边界匹配:/\bdef/匹配def和defghi等以def打头的单词,但不匹配abcdef。/def\b/匹配def和abcdef等以def结尾的单词,但不匹配defghi,/\bdef\b/只匹配字符串def。注意:/\bdef/可匹配$defghi,因为$并不被看作是单词的部分。
例4:\B在单词内部匹配:/\Bdef/匹配abcdef等,但不匹配def;/def\B/匹配defghi等;/\Bdef\B/匹配cdefg、abcdefghi等,但不匹配def,defghi,abcdef。
7、模式中的变量替换
将句子分成单词:
$pattern = "[\\t ]+";
@words = split(/$pattern/, $line);
8、字符范围转义E 转义字符 描述 范围
\d 任意数字 [0-9]
\D 除数字外的任意字符 [^0-9]
\w 任意单词字符 [_0-9a-zA-Z]
\W 任意非单词字符 [^_0-9a-zA-Z]
\s 空白 [ \r\t\n\f]
\S 非空白 [^ \r\t\n\f]
例:/[\da-z]/匹配任意数字或小写字母。
9、匹配任意字符
字符"."匹配除换行外的所有字符,通常与*合用。
10、匹配指定数目的字符
字符对{}指定所匹配字符的出现次数。如:/de{1,3}f/匹配def,deef和deeef;/de{3}f/匹配deeef;/de{3,}f/匹配不少于3个e在d和f之间;/de{0,3}f/匹配不多于3个e在d和f之间。
11、指定选项
字符"|"指定两个或多个选择来匹配模式。如:/def|ghi/匹配def或ghi。
例:检验数字表示合法性
if ($number =~ /^-?\d+$|^-?0[xX][\da-fa-F]+$/) {
print ("$number is a legal integer.\n";
} else {
print ("$number is not a legal integer.\n";
}
其中 ^-?\d+$ 匹配十进制数字,^-?0[xX][\da-fa-F]+$ 匹配十六进制数字。
12、模式的部分重用
当模式中匹配相同的部分出现多次时,可用括号括起来,用\n来多次引用,以简化表达式:
/\d{2}([\W])\d{2}\1\d{2}/ 匹配:
12-05-92
26.11.87
07 04 92等但不匹配12-05.92
注意:/\d{2}([\W])\d{2}\1\d{2}/ 不同于/(\d{2})([\W])\1\2\1/ ,后者只匹配形如17-17-17的字符串,而不匹配17-05-91等。
13、转义和特定字符的执行次序
象操作符一样,转义和特定字符也有执行次序:特殊字符 描述
() 模式内存
+ * ? {} 出现次数
^ $ \b \B 锚
| 选项
14、指定模式定界符
缺省的,模式定界符为反斜线/,但其可用字母m自行指定,如:
m!/u/jqpublic/perl/prog1! 等价于/\/u\/jqpublic\/perl\/prog1/
注:当用字母'作为定界符时,不做变量替换;当用特殊字符作为定界符时,其转义功能或特殊功能即不能使用。成对的符号必须成对地使用。
15、模式次序变量
在模式匹配后调用重用部分的结果可用变量$n,全部的结果用变量$&。匹配处之前的部分用变量$`,匹配处之前的部分用变量$'。也可用列表一次取得。
$string = "This string contains the number 25.11.";
$string =~ /-?(\d+)\.?(\d+)/; # 匹配结果为25.11
$integerpart = $1; # now $integerpart = 25
$decimalpart = $2; # now $decimalpart = 11
$totalpart = $&; # now totalpart = 25.11
$_ = "This string contains the number 25.11.";
@result =~ /-?(\d+)\.?(\d+)/;

四、模式匹配选项
1、匹配所有可能的模式(g选项)不加g只匹配一处。
@matches = "balata" =~ /.a/g; # now @matches = ("ba", "la", "ta"
匹配的循环:
while ("balata" =~ /.a/g) {
$match = $&;
print ("$match\n";
}
结果为:
ba
la
ta
当使用了选项g时,可用函数pos来控制下次匹配的偏移:
$offset = pos($string);
pos($string) = $newoffset;
2、忽略大小写(i选项)例
/de/i 匹配de,dE,De和DE。
3、将字符串看作多行(m选项)
在此情况下,^符号匹配字符串的起始或新的一行的起始;$符号匹配任意行的末尾。
4、只执行一次变量替换例
$var = 1;
$line = <STDIN>;
while ($var < 10) {
$result = $line =~ /$var/o;
$line = <STDIN>;
$var++;
}
每次均匹配/1/。
5、将字符串看作单行例
/a.*bc/s匹配字符串axxxxx \nxxxxbc,但/a.*bc/则不匹配该字符串。
6、在模式中忽略空格
/\d{2} ([\W]) \d{2} \1 \d{2}/x等价于/\d{2}([\W])\d{2}\1\d{2}/。
五、替换操作符
语法为s/pattern/replacement/,其效果为将字符串中与pattern匹配的部分换成replacement。如:
$string = "abc123def";
$string =~ s/123/456/; # now $string = "abc456def";
在替换部分可使用模式次序变量$n,如s/(\d+)/[$1]/,但在替换部分不支持模式的特殊字符,如{},*,+等,如s/abc/[def]/将把abc替换为[def]。
替换操作符的选项如下表:
选项 描述
g 改变模式中的所有匹配
i 忽略模式中的大小写
e 替换字符串作为表达式
m 将待匹配串视为多行
o 仅赋值一次
s 将待匹配串视为单行
x 忽略模式中的空白
注:e选项把替换部分的字符串看作表达式,在替换之前先计算其值,如:
$string = "0abc1";
$string =~ s/[a-zA-Z]+/$& x 2/e; # now $string = "0abcabc1"
六、翻译操作符
这是另一种替换方式,语法如:tr/string1/string2/。同样,string2为替换部分,但其效果是把string1中的第一个字符替换为string2中的第一个字符,把string1中的第二个字符替换为string2中的第二个字符,依此类推。如:
$string = "abcdefghicba";
$string =~ tr/abc/def/; # now string = "defdefghifed"
当string1比string2长时,其多余字符替换为string2的最后一个字符;当string1中同一个字符出现多次时,将使用第一个替换字符。
翻译操作符的选项如下:选项 描述
c 翻译所有未指定字符
d 删除所有指定字符
s 把多个相同的输出字符缩成一个
如$string =~ tr/\d/ /c;把所有非数字字符替换为空格。$string =~ tr/\t //d;删除tab和空格; $string =~ tr/0-9/ /cs;把数字间的其它字符替换为一个空格。
在替换中使用字符类型
$_="this is a test";
s/(\w+)/<$1>/g; # $_ is now "<this> <is> <a> <test>"
七、扩展模式匹配
PERL支持PERL4和标准UNIX模式匹配操作所没有的一些模式匹配能力。其语法为:(?<c>pattern),其中c是一个字符,pattern是起作用的模式或子模式。
1、不存贮括号内的匹配内容
在PERL的模式中,括号内的子模式将存贮在内存中,此功能即取消存贮该括号内的匹配内容,如/(?:a|b|c)(d|e)f\1/中的\1表示已匹配的d或e,而不是a或b或c。
2、内嵌模式选项
通常模式选项置于其后,有四个选项:i、m、s、x可以内嵌使用,语法为:/(?option)pattern/,等价于/pattern/option。
3、肯定的和否定的预见匹配
肯定的预见匹配语法为/pattern(?=string)/,其意义为匹配后面为string的模式,相反的,(?!string)意义为匹配后面非string的模式,如:
$string = "25abc8";
$string =~ /abc(?=[0-9])/;
$matched = $&; # $&为已匹配的模式,此处为abc,而不是abc8
4、模式注释
PERL5中可以在模式中用?#来加注释,如:
if ($string =~ /(?i)[a-z]{2,3}(?# match two or three alphabetic characters)/ {
...
}
第七章 控制结构
一、条件判断
二、循环:
1、while循环
2、until循环
3、for循环
4、针对列表(数组)每个元素的foreach循环
5、do循环
6、循环控制
7、传统的goto语句
三、单行条件
一、条件判断
if ( <expression> {
<statement_block_1>
}
elsif ( <expression> ) {
<statement_block_2>
}
...
else{
<statement_block_3>
}
二、循环:
1、while循环
while ( <expression> ) {
<statement_block>
}
2、until循环
until ( <expression> ) {
<statement_block>
}
3、类C的for循环 ,如
for ($count=1; $count <= 5; $count++) {
# statements inside the loop go here
}
下面是在for循环中使用逗号操作符的例子:
for ($line = <STDIN>, $count = 1; $count <= 3; $line = <STDIN>, $count++) {
print ($line);
}
它等价于下列语句:
$line = <STDIN>;
$count = 1;
while ($count <= 3) {
print ($line);
$line = <STDIN>;
$count++;
}
4、针对列表(数组)每个元素的循环:foreach, 语法为:
foreach localvar (listexpr) {
statement_block;
}
例:
foreach $word (@words) {
if ($word eq "the" {
print ("found the word 'the'\n";
}
}
注:
(1)此处的循环变量localvar是个局部变量,如果在此之前它已有值,则循环后仍恢复该值。
(2)在循环中改变局部变量,相应的数组变量也会改变,如:
@list = (1, 2, 3, 4, 5);
foreach $temp (@list) {
if ($temp == 2) {
$temp = 20;
}
}
此时@list已变成了(1, 20, 3, 4, 5)。
5、do循环
do {
statement_block
} while_or_until (condexpr);
do循环至少执行一次循环。
6、循环控制
退出循环为last,与C中的break作用相同;
执行下一个循环为next,与C中的continue作用相同;
PERL特有的一个命令是redo,其含义是重复此次循环,即循环变量不变,回到循环起始点,但要注意,redo命令在do循环中不起作用。
一次跳出多个循环,只要为想去的代码段取个名字(label),再配合 last,next,redo 即可,例:
OUTER: for(...){
INNER: for(...){
last OUTER;
}
}
7、传统的goto label;语句。
三、单行条件
语法为statement keyword condexpr。其中keyword可为if、unless、while或until,如:
print ("This is zero.\n" if ($var == 0);
print ("This is zero.\n" unless ($var != 0);
print ("Not zero yet.\n" while ($var-- > 0);
print ("Not zero yet.\n" until ($var-- == 0);
虽然条件判断写在后面,但却是先执行的。
第八章 子程序
一、定义
二、调用
1、用&调用
2、先定义后调用
3、前向引用
4、用do调用
三、返回值
四、局部变量
五、子程序参数传递
1、形式
2、传送数组
六、递归子程序
七、用别名传递数组参数
八、预定义的子程序
一、定义
子程序即执行一个特殊任务的一段分离的代码,它可以使减少重复代码且使程序易读。PERL中,子程序可以出现在程序的任何地方。定义方法为:
sub subroutine{
statements;
}
二、调用
调用方法如下:
1、用&调用
&subname;
...
sub subname{
...
}
2、先定义后调用 ,可以省略&符号
sub subname{
...
}
...
subname;
3、前向引用 ,先定义子程序名,后面再定义子程序体
sub subname;
...
subname;
...
sub subname{
...
}
4、用do调用
do my_sub(1, 2, 3);等价于&my_sub(1, 2, 3);
三、返回值
缺省的,子程序中最后一个语句的值将用作返回值。语句return (retval);也可以推出子程序并返回值retval,retval可以为列表。
四、局部变量
子程序中局部变量的定义有两种方法:my和local。其区别是:my定义的变量只在该子程序中存在;而local定义的变量不存在于主程序中,但存在于该子程序和该子程序调用的子程序中(在PERL4中没有my)。定义时可以给其赋值,如:
my($scalar) = 43;
local(@array) = (1, 2, 3);
五、子程序参数传递
1、形式
&sub1(&number1, $number2, $nubmer3);
...
sub sub1{
my($number1, $number2, $number3) = @_;
...
}
2、传送数组
&addlist (@mylist);
&addlist ("14", "6", "11";
&addlist ($value1, @sublist, $value2);
...
sub addlist {
my (@list) = @_;
...
}
参数为数组时,子程序只将它赋给一个数组变量。如
sub twolists {
my (@list1, @list2) = @_;
}
中@list2必然为空。但简单变量和数组变量可以同时传递:
&twoargs(47, @mylist); # 47赋给$scalar,@mylist赋给@list
&twoargs(@mylist); # @mylist的第一个元素赋给$scalar,其余的元素赋给@list
...
sub twoargs {
my ($scalar, @list) = @_;
...
}
六、递归子程序
PERL中,子程序可以互相调用,其调用方法与上述相同,当调用该子程序本身时,即成了递归子程序。递归子程序有两个条件:1、除了不被子程序改变的变量外,所有的变量必须的局部的;2、该子程序要含有停止调用本身的代码。
七、用别名传递数组参数
1、用前面讲到的调用方法&my_sub(@array)将把数组@array的数据拷贝到子程序中的变量@_中,当数组很大时,将会花费较多的资源和时间,而用别名传递将不做这些工作,而对该数组直接操作。形式如:
@myarray = (1, 2, 3, 4, 5);
&my_sub(*myarray);
sub my_sub {
my (*subarray) = @_;
}
2、此方法类似于C语言中的传递数组的起始地址指针,但并不一样,在定义数组的别名之后,如果有同名的简单变量,则对该变量也是起作用的。如:
$foo = 26;
@foo = ("here's", "a", "list";
&testsub (*foo);
...
sub testsub {
local (*printarray) = @_;
...
$printarray = 61;
}
当子程序执行完,主程序中的$foo的值已经成了61,而不再是26了。
3、用别名的方法可以传递多个数组,如:
@array1 = (1, 2, 3);
@array2 = (4, 5, 6);
&two_array_sub (*array1, *array2);
sub two_array_sub {
my (*subarray1, *subarray2) = @_;
}
在该子程序中,subarray1是array1的别名,subarray2是array2的别名。
八、预定义的子程序
PERL5预定义了三个子程序,分别在特定的时间执行,它们是:BEGIN子程序在程序启动时被调用;END子程序在程序结束时被调用;AUTOLOAD子程序在找不到某个子程序时被调用。你可以自己定义它们,以在特定时间执行所需要的动作。如:
BEGIN {
print("Hi! Welcome to Perl!\n";
}
AUTOLOAD{
print("subroutine $AUTOLOAD not found\n"; # 变量$AUTOLOAD即未找到的子程序名
print("arguments passed: @_\n";
}
若同一个预定义子程序定义了多个,则BEGIN顺序执行,END逆序执行。
第九章 关联数组/哈希表
一、数组变量的限制
二、定义
三、访问关联数组的元素
四、增加元素
五、创建关联数组
六、从数组变量复制到关联数组
七、元素的增删
八、列出数组的索引和值
九、用关联数组循环
十、用关联数组创建数据结构
1、(单)链表
2、结构
3、树
一、数组变量的限制
在前面讲的数组变量中,可以通过下标访问其中的元素。例如,下列语句访问数组@array的第三个元素:
$scalar = $array[2];
虽然数组很有用,但它们有一个显著缺陷,即很难记住哪个元素存贮的什么内容。假如我们来写一个程序计算某文件中首字母大写的单词出现的次数,用数组来实现就比较困难,程序代码如下:
1 : #!/usr/local/bin/perl
2 :
3 : while ($inputline = <STDIN> {
4 : while ($inputline =~ /\b[A-Z]\S+/g) {
5 : $word = $&;
6 : $word =~ s/[;.,:-]$//; # remove punctuation
7 : for ($count = 1; $count <= @wordlist;
8 : $count++) {
9 : $found = 0;
10: if ($wordlist[$count-1] eq $word) {
11: $found = 1;
12: $wordcount[$count-1] += 1;
13: last;
14: }
15: }
16: if ($found == 0) {
17: $oldlength = @wordlist;
18: $wordlist[$oldlength] = $word;
19: $wordcount[$oldlength] = 1;
20: }
21: }
22: }
23: print ("Capitalized words and number of occurrences:\n";
24: for ($count = 1; $count <= @wordlist; $count++) {
25: print ("$wordlist[$count-1]: $wordcount[$count-1]\n";
26: }
运行结果如下:
Here is a line of Input.
This Input contains some Capitalized words.
^D
Capitalized words and number of occurrences:
Here: 1
Input: 2
This: 1
Capitalized: 1
这个程序每次从标准输入文件读一行文字,第四行起的循环匹配每行中首字母大写的单词,每找到一个循环一次,赋给简单变量$word。在第六行中去掉标点后,查看该单词是否曾出现过,7~15行中在@wordlist中挨个元素做此检查,如果某个元素与$word相等,@wordcount中相应的元素就增加一个数。如果没有出现过,即@wordlist中没有元素与$word相等,16~20行给@wordlist和@wordcount增加一个新元素。
二、定义
正如你所看到的,使用数组元素产生了一些问题。首先,@wordlist中哪个元素对应着哪个单词并不明显;更糟的是,每读进一个新单词,程序必须检查整个列表才能知道该单词是否曾经出现过,当列表变得较大时,这是很耗费时间的。
这些问题产生的原因是数组元素通过数字下标访问,为了解决这类问题,Perl定义了另一种数组,可以用任意简单变量值来访问其元素,这种数组叫做关联数组,也叫哈希表。
为了区分关联数组变量与普通的数组变量,Perl使用%作为其首字符,而数组变量以@打头。与其它变量名一样,%后的第一个字符必须为字母,后续字符可以为字母、数字或下划线。
三、访问关联数组的元素
关联数组的下标可以为任何简单/标量值,访问单个元素时以$符号打头,下标用大括号围起来。例如:
$fruit{"bananas"}
$number{3.14159}
$integer{-7}
简单变量也可作为下标,如:
$fruit{$my_fruit}
四、增加元素
创建一个关联数组元素最简单的方法是赋值,如语句$fruit{"bananas"} = 1; 把1赋给关联数组%fruit下标为bananas的元素,如果该元素不存在,则被创建,如果数组%fruit从未使用过,也被创建。
这一特性使得关联数组很容易用于计数。下面我们用关联数组改写上面的程序,注意实现同样的功能此程序简化了许多。
1 : #!/usr/local/bin/perl
2 :
3 : while ($inputline = ) {
4 : while ($inputline =~ /\b[A-Z]\S+/g) {
5 : $word = $&;
6 : $word =~ s/[;.,:-]$//; # remove punctuation
7 : $wordlist{$word} += 1;
8 : }
9 : }
10: print ("Capitalized words and number of occurrences:\n";
11: foreach $capword (keys(%wordlist)) {
12: print ("$capword: $wordlist{$capword}\n";
13: }
运行结果如下:
Here is a line of Input.
This Input contains some Capitalized words.
^D
Capitalized words and number of occurrences:
This: 1
Input: 2
Here: 1
Capitalized: 1
你可以看到,这次程序简单多了,读取输入并存贮各单词数目从20行减少到了7行。
本程序用关联数组%wordlist跟踪首字母大写的单词,下标就用单词本身,元素值为该单词出现的次数。第11行使用了内嵌函数keys()。这个函数返回关联数组的下标列表,foreach语句就用此列表循环。
注:关联数组总是随机存贮的,因此当你用keys()访问其所有元素时,不保证元素以任何顺序出现,特别值得一提的是,它们不会以被创建的顺序出现。
要想控制关联数组元素出现的次序,可以用sort()函数对keys()返回值进行排列,如:
foreach $capword (sort keys(%wordlist)) {
print ("$capword: $wordlist{$capword}\n";
}
五、创建关联数组
可以用单个赋值语句创建关联数组,如:
%fruit = ("apples",17,"bananas",9,"oranges","none";
此语句创建的关联数组含有下面三个元素:
下标为apples的元素,值为17
下标为bananas的元素,值为9
下标为oranges的元素,值为none
注:用列表给关联数组赋值时,Perl5允许使用"=>"或","来分隔下标与值,用"=>"可读性更好些,上面语句等效于:
%fruit = ("apples"=>17,"bananas"=>9,"oranges"=>"none";
六、从数组变量复制到关联数组
与列表一样,也可以通过数组变量创建关联数组,当然,其元素数目应该为偶数,如:
@fruit = ("apples",17,"bananas",9,"oranges","none";
%fruit = @fruit;
反之,可以把关联数组赋给数组变量,如:
%fruit = ("grapes",11,"lemons",27);
@fruit = %fruit;
注意,此语句中元素次序未定义,那么数组变量@fruit可能为("grapes",11,"lemons",27)或("lemons",27,"grapes",11)。
关联数组变量之间可以直接赋值,如:%fruit2 = %fruit1; 还可以把数组变量同时赋给一些简单变量和一个关联数组变量,如:
($var1, $var2, %myarray) = @list;
此语句把@list的第一个元素赋给$var1,第二个赋给$var2,其余的赋给%myarray。
最后,关联数组可以通过返回值为列表的内嵌函数或用户定义的子程序来创建,下例中把split()函数的返回值--一个列表--赋给一个关联数组变量。
1: #!/usr/local/bin/perl
2:
3: $inputline = <STDIN>;
4: $inputline =~ s/^\s+|\s+\n$//g;
5: %fruit = split(/\s+/, $inputline);
6: print ("Number of bananas: $fruit{\"bananas\"}\n";
运行结果如下:
oranges 5 apples 7 bananas 11 cherries 6
Number of bananas: 11
七、元素的增删
增加元素已经讲过,可以通过给一个未出现过的元素赋值来向关联数组中增加新元素,如$fruit{"lime"} = 1;创建下标为lime、值为1的新元素。
删除元素的方法是用内嵌函数delete,如欲删除上述元素,则:
delete ($fruit{"lime"});
注意:
1、一定要使用delete函数来删除关联数组的元素,这是唯一的方法。
2、一定不要对关联数组使用内嵌函数push、pop、shift及splice,因为其元素位置是随机的。
八、列出数组的索引和值
上面已经提到,keys()函数返回关联数组下标的列表,如:
%fruit = ("apples", 9,
"bananas", 23,
"cherries", 11);
@fruitsubs = keys(%fruits);
这里,@fruitsubs被赋给apples、bananas、cherries构成的列表,再次提请注意,此列表没有次序,若想按字母顺序排列,可使用sort()函数。
@fruitindexes = sort keys(%fruits);
这样结果为("apples","bananas","cherries"。类似的,内嵌函数values()返回关联数组值的列表,如:
%fruit = ("apples", 9,
"bananas", 23,
"cherries", 11);
@fruitvalues = values(%fruits);
这里,@fruitvalues可能的结果为(9,23.11),次序可能不同。
九、用关联数组循环
前面已经出现过利用keys()函数的foreach循环语句,这种循环效率比较低,因为每返回一个下标,还得再去寻找其值,如:
foreach $holder (keys(%records)){
$record = $records{$holder};
}
Perl提供一种更有效的循环方式,使用内嵌函数each(),如:
%records = ("Maris", 61, "Aaron", 755, "Young", 511);
while (($holder, $record) = each(%records)) {
# stuff goes here
}
each()函数每次返回一个双元素的列表,其第一个元素为下标,第二个元素为相应的值,最后返回一个空列表。
注意:千万不要在each()循环中添加或删除元素,否则会产生不可预料的后果。
十、用关联数组创建数据结构
用关联数组可以模拟在其它高级语言中常见的多种数据结构,本节讲述如何用之实现:链表、结构和树。
1、(单)链表
链表是一种比较简单的数据结构,可以按一定的次序存贮值。每个元素含有两个域,一个是值,一个是引用(或称指针),指向链表中下一个元素。一个特殊的头指针指向链表的第一个元素。
在Perl中,链表很容易用关联数组实现,因为一个元素的值可以作为下一个元素的索引。下例为按字母顺序排列的单词链表:
%words = ("abel", "baker",
"baker", "charlie",
"charlie", "delta",
"delta", "";
$header = "abel";
上例中,简单变量$header含有链表中第一个单词,它同时也是关联数组第一个元素的下标,其值baker又是下一个元素的下标,依此类推。
下标为delta的最后一个元素的值为空串,表示链表的结束。
在将要处理的数据个数未知或其随程序运行而增长的情况下,链表十分有用。下例用链表按字母次序输出一个文件中的单词。
1 : #!/usr/local/bin/perl
2 :
3 : # initialize list to empty
4 : $header = "";
5 : while ($line = <STDIN> {
6 : # remove leading and trailing spaces
7 : $line =~ s/^\s+|\s+$//g;
8 : @words = split(/\s+/, $line);
9 : foreach $word (@words) {
10: # remove closing punctuation, if any
11: $word =~ s/[.,;:-]$//;
12: # convert all words to lower case
13: $word =~ tr/A-Z/a-z/;
14: &add_word_to_list($word);
15: }
16: }
17: &print_list;
18:
19: sub add_word_to_list {
20: local($word) = @_;
21: local($pointer);
22:
23: # if list is empty, add first item
24: if ($header eq "" {
25: $header = $word;
26: $wordlist{$word} = "";
27: return;
28: }
29: # if word identical to first element in list,
30: # do nothing
31: return if ($header eq $word);
32: # see whether word should be the new
33: # first word in the list
34: if ($header gt $word) {
35: $wordlist{$word} = $header;
36: $header = $word;
37: return;
38: }
39: # find place where word belongs
40: $pointer = $header;
41: while ($wordlist{$pointer} ne "" &&
42: $wordlist{$pointer} lt $word) {
43: $pointer = $wordlist{$pointer};
44: }
45: # if word already seen, do nothing
46: return if ($word eq $wordlist{$pointer});
47: $wordlist{$word} = $wordlist{$pointer};
48: $wordlist{$pointer} = $word;
49: }
50:
51: sub print_list {
52: local ($pointer);
53: print ("Words in this file:\n";
54: $pointer = $header;
55: while ($pointer ne "" {
56: print ("$pointer\n";
57: $pointer = $wordlist{$pointer};
58: }
59: }
运行结果如下:
Here are some words.
Here are more words.
Here are still more words.
^D
Words in this file:
are
here
more
some
still
words
此程序分为三个部分:
主程序:读取输入并转换到相应的格式。
子程序:add_word_to_list,建立排序单词链表。
子程序:print_list,输出单词链表
第3~17行为主程序,第4行初始化链表,将表头变量$header设为空串,第5行起的循环每次读取一行输入,第7行去掉头、尾的空格,第8行将句子分割成单词。9~15行的内循环每次处理一个单词,如果该单词的最后一个字符是标点符号,就去掉。第13行把单词转换成全小写形式,第14行传递给子程序add_word_to_list。
子程序add_word_to_list先在第24行处检查链表是否为空。如果是,第25行将单词赋给$header,26行创建链表第一个元素,存贮在关联数组%wordlist中。如果链表非空,37行检查第一个元素是否与该单词相同,如果相同,就立刻返回。下一步检查这一新单词是否应该为链表第一个元素,即其按字母顺序先于$header。如果是这样,则:
1、创建一个新元素,下标为该新单词,其值为原第一个单词。
2、该新单词赋给$header。
如果该新单词不该为第一个元素,则40~44行利用局域变量$pointer寻找其合适的有效位置,41~44行循环到$wordlist{$pointer}大于或等于$word为止。接下来46行查看该单词是否已在链表中,如果在就返回,否则47~48行将其添加到链表中。首先47行创建新元素$wordlist{$word},其值为$wordlist{$pointer},这时$wordlist{$word}和$wordlist{$pointer}指向同一个单词。然后,48行将$wordlist{$pointer}的值赋为$word,即将$wordlist{$pointer}指向刚创建的新元素$wordlist{$word}。
最后当处理完毕后,子程序print_list()依次输出链表,局域变量$pointer含有正在输出的值,$wordlist{$pointer}为下一个要输出的值。
注:一般不需要用链表来做这些工作,用sort()和keys()在关联数组中循环就足够了,如:
foreach $word (sort keys(%wordlist)) {
# print the sorted list, or whatever }
但是,这里涉及的指针的概念在其它数据结构中很有意义。
2、结构
许多编程语言可以定义结构(structure),即一组数据的集合。结构中的每个元素有其自己的名字,并通过该名字来访问。
Perl不直接提供结构这种数据结构,但可以用关联数组来模拟。例如模拟C语言中如下的结构:
struce{
int field1;
int field2;
int field3; }mystructvar;
我们要做的是定义一个含有三个元素的关联数组,下标分别为field1、field2、field3,如:
%mystructvar = ("field1" , "" ,
"field2" , "" ,
"field3" , "" ,);
像上面C语言的定义一样,这个关联数组%mystrctvar有三个元素,下标分别为field1、field2、field3,各元素初始值均为空串。对各元素的访问和赋值通过指定下标来进行,如:
$mystructvar{"field1"} = 17;
3、树
另一个经常使用的数据结构是树。树与链表类似,但每个节点指向的元素多于一个。最简单的树是二叉树,每个节点指向另外两个元素,称为左子节点和右子节点(或称孩子),每个子节点又指向两个孙子节点,依此类推。
注:此处所说的树像上述链表一样是单向的,每个节点指向其子节点,但子节点并不指向父节点。
树的概念可以如下描述:
因为每个子节点均为一个树,所以左/右子节点也称为左/右子树。(有时称左/右分支)
第一个节点(不是任何节点的子节点的节点)称为树的根。
没有孩子(子节点)的节点称为叶节点。
有多种使用关联数组实现树结构的方法,最好的一种应该是:给子节点分别加上left和right以访问之。例如,alphaleft和alpharight指向alpha的左右子节点。下面是用此方法创建二叉树并遍历的例程:
1 : #!/usr/local/bin/perl
2 :
3 : $rootname = "parent";
4 : %tree = ("parentleft", "child1",
5 : "parentright", "child2",
6 : "child1left", "grandchild1",
7 : "child1right", "grandchild2",
8 : "child2left", "grandchild3",
9 : "child2right", "grandchild4";
10: # traverse tree, printing its elements
11: &print_tree($rootname);
12:
13: sub print_tree {
14: local ($nodename) = @_;
15: local ($leftchildname, $rightchildname);
16:
17: $leftchildname = $nodename . "left";
18: $rightchildname = $nodename . "right";
19: if ($tree{$leftchildname} ne "" {
20: &print_tree($tree{$leftchildname});
21: }
22: print ("$nodename\n";
23: if ($tree{$rightchildname} ne "" {
24: &print_tree($tree{$rightchildname});
25: }
26: }
结果输出如下:
grandchild1
child1
grandchild2
parent
grandchild3
child2
grandchild4
该程序创建的二叉树如下图:
注意函数print_tree()以次序“左子树、节点、右子树”来输出各节点的名字,这种遍历次序称为“左序遍历”。如果把第22行移到19行前,先输出节点明,再输出左子树、右子树,则为“中序遍历”,如果把第22行移到25行后,输出次序为左子树、右子树、节点,则为“右序遍历”。
可以用同样的方法,即连接字符串构成下标,来创建其它的数据结构,如数据库等。
第十章 格式化输出
一、定义打印格式
二、显示打印格式
三、在打印格式中显示值
1、通用的打印格式
2、格式和局域变量
3、选择值域格式
4、输出值域字符
四、输出到其它文件
五、分页
六、格式化长字符串
七、用printf格式化输出
我们已经见过用print函数将原始的未格式化的文本输出到文件,本章讲述如何用函数write和打印格式来生成格式化的输出。
一、定义打印格式
定义打印格式的关键字为format,语法为:
format formatname =
lines_of_output
.
打印格式名formatname的命名规则与一般变量相同。结束行为一个句号,此行不能有任何其它字符,包括空格,句号必须是该行第一个字符。
打印格式的定义象子程序一样可以放在任何地方,甚至可以放在条件语句中,但是最好把它们集中放在程序的开头或结尾。
二、显示打印格式
打印格式的显示有两步:
1、将系统变量$~设成所要使用的格式
2、调用函数write
例如:
1 : #!/usr/local/bin/perl
2 :
3 : $~ = "MYFORMAT";
4 : write;
5 :
6 : format MYFORMAT =
7 : ===================================
8 : Here is the text I want to display.
9 : ===================================
10: .
结果输出如下:
$ program
===================================
Here is the text I want to display.
===================================
$
如果不用$~指定打印格式,Perl解释器就假定要使用的格式名与要写入的文件变量同名,在本例中,如果不指定使用MYFORMAT,则Perl解释器试图使用名为STDOUT的打印格式。
三、在打印格式中显示值
我们使用打印格式的主要原因当然是格式化存贮在简单变量或数组变量中的值从而生成可读性好的输出,这一目的用“值域”来实现。每个值域指定一个值,如变量或表达式,调用write函数时,该值就以值域指定的格式显示。
1、通用的打印格式
打印格式的一个缺点是定义中包含了变量名,例如:
format MYFORMAT =
==========================================================
The winning number is @<<<<<<!
$winnum
==========================================================
.
当调用write输出此格式时,必须记着它使用了变量$winnum。用子程序和局域变量就可以创建更通用的打印格式。下例从STDIN输入一个文件并输出五个出现频率最高的字母及出现次数。
1 : #!/usr/local/bin/perl
2 :
3 : while ($line = ) {
4 : $line =~ tr/A-Z/a-z/;
5 : $line =~ s/[^a-z]//g;
6 : @letters = split(//, $line);
7 : foreach $letter (@letters) {
8 : $lettercount{$letter} += 1;
9 : }
10: }
11:
12: $~ = "WRITEHEADER";
13: write;
14: $count = 0;
15: foreach $letter (reverse sort occurrences
16: (keys(%lettercount))) {
17: &write_letter($letter, $lettercount{$letter});
18: last if (++$count == 5);
19: }
20:
21: sub occurrences {
22: $lettercount{$a} <=> $lettercount{$b};
23: }
24: sub write_letter {
25: local($letter, $value) = @_;
26:
27: $~ = "WRITELETTER";
28: write;
29: }
30: format WRITEHEADER =
31: The five most frequently occurring letters are:
32: .
33: format WRITELETTER =
34: @: @<<<<<<
35: $letter, $value
36: .
运行结果如下:
$ program
This is a test file.
This test file contains some input.
The quick brown fox jumped over the lazy dog.
^D
The five most frequently occurring letters are:
t: 10
e: 9
i: 8
s: 7
o: 6
$
2、格式和局域变量
在上例中,你可能已经注意到子程序write_letter调用write输出字母及其出现次数,即使格式定义在子程序外部仍能正常工作。在第17行中将字母及其出现次数传递给该子程序,在子程序中,打印格式使用局域变量$letter和$value,这样保证了在foreach循环中每次输出当前的字母和值。
然而要注意的是,使用my定义的局域变量要求格式定义在子程序内部,否则就不会输出,因此,用write输出的局域变量一定要用local定义。(local和my详见《子程序》一章)
注:Perl4中没有my函数,故不会有此问题。
3、选择值域格式
我们已经知道了打印格式和write函数怎么工作,现在来看看值域的格式,见下表:
格式 值域含义
@<<< 左对齐输出
@>>> 右对齐输出
@||| 中对齐输出
@##.## 固定精度数字
@* 多行文本
每个值域的第一个字符是行填充符,当使用@字符时,不做文本格式化。对文本的格式化稍后来讲。
在上表中,除了多行值域@*,域宽都等于其指定的包含字符@在内的字符个数,例如:
@###.##
表示七个字符宽,小数点前四个,小数点后两
<---- 以上言论仅代表本人立场 ---->



__________________
- Is life always this hard,or it just when you're a kid?
- Always like this.  
   

不累的王

普通会员

发贴: 339
积分: 0
来自:
注册日期: 2006-05-26
  发表时间: 2006-09-22 16:11:32  

--------------------------------------------------------------------------------
这里有不错的Perl例子:
http://pleac.sourceforge.net/pleac_perl/index.html


每次学Perl都是半途而废...--!~

<---- 以上言论仅代表本人立场 ---->



__________________


  “……机器人罗诺,现在我要对你下达最新指令了。”
  “是,主人。我都等了三万年了呢。”
  “你还记得回地球的航路吧?”
您需要登录后才可以回帖 登录 | 注册创意安天

本版积分规则

小黑屋|手机版|Archiver|创意安天 ( 京ICP备09068574,ICP证100468号。 )

GMT+8, 2024-5-18 12:42

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表