在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
六、子程序的引用
&$severe("Divide by zero"); 结果输出如下:
上例中,子程序errorMsg使用了局域变量$lvl,用于返回给调用者。当errorMsg被调用时,$lvl的值设置到返回的子程序内容中,虽然是用的my函数。三次调用设置了三个不同的$lvl变量值。当errorMsg返回时,$lvl的值保存到每次被声明时所产生的子程序代码中。最后三句对产生的子程序引用进行调用时$msg的值被替换,但$lvl的值仍是相应子程序代码创建时的值。
结果输出如下:
1、当想传递给子程序的参数是多于一个的数组时一定要使用引用。
八、文件句柄的引用
注意其中文件句柄引用的语法为*FILEHANDLE。 注: 如果你的e文好, 那就看一看PERL的MAN手册吧 那里面什么都有PERL 中的模块 结构很简单 用起来也不难 但至少我不是很喜欢它 注意 PERL中的模块和类是同意词
2 一个类是一个包 它提供了一些方法 3 一个方法是一个SUB 它的第一个参数为一个类的引用
我们所使用的类的引用一般是一个HASH的引用 通过调用函数bless 将它同一个类绑在一起
Critter.pm package Critter sub new { bless {}; } 本例中返回了一个空的HASH表(PERL中也叫做匿名表)
try.pm
sub new { $para = @_; %hash = {};
bless %hash, try; return %hash; }
print shift; print ("\n", @_); }
a.pl // 它与try.pm放在同一个目录下 use try;
$m_try->pp("hello world");
my $this = shift; my $class = ref($this) || $this; my $self = {}; bless $self, $class $self->initialize(); return $self; }
3 new try(a => "hello"); 第二个和第三个是一样的 2,3 中函数new的参数是 ('try', 'a', 'hello') 1 的参数却是('a', 'hello');
#这与class是什么东西无关 如同 $m_class->method(arg)被转换为 #class::method($m_class,arg)一样
当调用一个类中的方法时 第一个参数为HASH表的引用
sub pp { $class = shift; print (@_, $class->{'a'}); }
$m_try->pp("ha ha ha");
下面看一看 PERL中类的继承 PERL的类继承很奇怪 子类的目录比父类低一层
Tk.pm Tk/Button.pm
package Tk::Button.pm ... ...
这是一个特殊的类 所有的类都是它的子类 它定义了如下的方法
can 是否存在某个方法 VERSION 判断版本号
如: $self->SUPER::get();
package try; $VERSION = "1.00"; sub new { bless {} ,try; } sub pp { print "this is in father class\n"; }
package tt; use trk; @ISA = qw(try); #这样在tt中找不到的方法 会在try中查找 #如同tt是try的子类 sub new { bless {} ,tt; } sub pp { print "this is in son class\n"; }
#!/usr/bin/perl use tt; $m_tt = new tt; $m_tt->pp;
"this is in father class" 原因是调用了try中的new, 引用被bless成了try的.
sub pp { $self = shift; $self->SUPER::pp; #调用父类中的方法 } 会显示"this is in father" 正则表达式中的常用模式 正则表达式的 8 大原则 正则表达式是 Perl 语言的一大特色,也是 Perl 程序中的一点难点,不过如果大家能够很好的掌握他,就可以轻易地用正则表达式来完成字符串处理的任务,当然在 CGI 程序设计中就更能得心应手了。下面我们列出一些正则表达式书写时的一些基本语法规则。
9.1 正则表达式的三种形式 匹配:m/<regexp>/ (还可以简写为 /<regexp>/ ,略去 m) 替换:s/<pattern>/<replacement>/ 转化:tr/<pattern>/<replacemnt>/ 这三种形式一般都和 =~ 或 !~ 搭配使用(其中 "=~" 表示相匹配,在整条语句中读作 does,"!~" 表示不匹配,在整条语句中读作 doesn't),并在左侧有待处理的标量变量。如果没有该变量和 =~ !~ 操作符,则默认为处理 $_ 变量中的内容。举例如下: $str = "I love Perl"; 另外还有: foreach (@array) { s/a/b/; } # 此处每次循环将从 @array 数组中取出一个元素存放在 $_ 变量中,并对 $_ 进行替换处理。 Perl 的正则表达式中如果出现 () ,则发生匹配或替换后 () 内的模式被 Perl 解释器自动依次赋给系统 $1, $2 ...... 请看下面的例子: $string = "I love perl"; 替换操作 s/<pattern>/<replacement>/ 还可以在末尾加上 e 或 g 参数,他们的含义分别为: s/<pattern>/<replacement>/g 表示把待处理字符串中所有符合 <pattern> 的模式全部替换为 <replacement> 字符串,而不是只替换第一个出现的模式。 比如下面的例子: $string = "i:love:perl"; 下面给出一个完整的例子: #!/usr/bin/perl print"请输入一个字符串!\n"; 如果输入的字符串含有 perl 这个字符串的话,就会显示后面的提示信息。 9.2 正则表达式中的常用模式 /pattern/ 结果
范例 说明 9.3 正则表达式的八大原则 正则表达式是初学 Perl 的难点所在,不过只要一旦掌握其语法,你就可以拥有几乎无限的模式匹配能力,而且 Perl 编程的大部分工作都是掌握常规表达式。下面给大家介绍几条正则表达式使用过程中的 8 大原则。 正则表达式在对付数据的战斗中可形成庞大的联盟——这常常是一场战争。我们要记住下面八条原则: · 原则1:正则表达式有三种不同形式(匹配(m/ /),替换(s/ / /eg)和转换(tr/ / /))。 · 原则2:正则表达式仅对标量进行匹配( $scalar =~ m/a/; 可以工作; @array =~ m/a/ 将把@array作为标量对待,因此可能不会成功)。 · 原则3:正则表达式匹配一个给定模式的最早的可能匹配。缺省时,仅匹配或替换正则表达式一次( $a = 'string string2'; $a =~ s/string/ /; 导致 $a = 'string 2')。 · 原则4:正则表达式能够处理双引号所能处理的任意和全部字符( $a =~ m/$varb/ 在匹配前把varb扩展为变量;如果 $varb = 'a' $a = 'as',$a =~ s/$varb/ /; 等价于 $a =~ s/a/ /; ,执行结果使 $a = " s" )。 · 原则5:正则表达式在求值过程中产生两种情况:结果状态和反向引用: $a=~ m/pattern/ 表示 $a 中是否有子串 pattern 出现,$a =~ s/(word1)(word2)/$2$1/ 则“调换”这两个单词。 · 原则6:正则表达式的核心能力在于通配符和多重匹配运算符以及它们如何操作。$a =~ m/\w+/ 匹配一个或多个单词字符;$a =~ m/\d/" 匹配零个或多个数字。 · 原则7:如果欲匹配不止一个字符集合,Perl使用 "|" 来增加灵活性。如果输入 m/(cat|dog)/ 则相当于“匹配字符串 cat 或者 dog。 · 原则8:Perl用 (?..) 语法给正则表达式提供扩展功能。(这一点请同学们课后看相关资料) 想要学习所有这些原则?我建议大家先从简单的开始,并且不断的尝试和实验。实际上如果学会了 $a =~ m/ERROR/ 是在 $a 中查找子串ERROR,那么你就已经比在 C 这样的低层语言中得到了更大的处理能力。 变量名字
数值变量以 '$' 打头, 当引用数组中的一个元素时也一样. 意思是"这". 举例: $days # 数值变量 &quot;days&quot; $days[28] # 数组 @days 的第29个元素 $days{'Feb'} # 哈希表 %days 中 'Feb' 代表的数值 $#days # 数组 @days 的最大下标 当表示整个数组或数组的一部分时, 用 '@' 打头, 意思是 "这些" 或 "那些" @days # ($days[0], $days[1],... $days[n]) @days[3,4,5] # 即 @days[3..5] @days{'a','c'} # 即 ($days{'a'},$days{'c'}) 当表示整个哈希表时用 '%' 打头: %days # (key1, val1, key2, val2 ...) 此外, 子过程用 '&' 打头, 当不致引起混淆的时候可以省略. 符号表表项用 '*' 打头, 手册后面部分有详细说明. 不同的变量类型有自己的名字空间. 为一个数值变量, 一个数组和一个哈希表(一个文件句柄, 一个子过程, 一个标号)取相同的名字并不会引起冲突. 也就是说, $foo 和 @foo 是两个不同的变量. $foo[1] 是 @foo 的一部分, 而不是 $foo 的一部分. 看起来也许有点怪, 但要习惯它. 既然变量和数组名都是以 '$', '@', 或 '%' 打头, 那些 ``保留字'' 实际上并非对变量而言. (它们实际上是对标号和文件句柄而言, 标号和文件句柄是没有特殊的打头字母. 给一个文件句柄取名 ``log'' 是错误的. 应该用 open(LOG,'logfile') 而非 open(log,'logfile') . 用大写字母来表示文件句柄也增加了可读性, 避免和将来出现的保留字冲突.) 大小写是区分的 -- ``FOO'', ``Foo'' 和 ``foo'' 是完全不同的名字. 以字母或下划线开头的名字可以包含数字和下划线. 一个以字母数字组成的名字代表的变量可以用一个返回同类型的引用的表达式代替, 详见 perlref . 数字开头的名字只能包含更多的数字. 不是以字母, 数字, 下划线开头的名字只能有一个字符. 例如: $% 或 $$ . (大部分这些单字符名字是 Perl 的预定义变量, 例如, $$ 是当前进程号.) 变量名字 与此相对的, 一个操作会为它的每一个参数确定上下文. 例如 int( <stdin>) 取整数操作为 <STDIN> 提供了一个数值上下文, <STDIN> 在 STDIN 读入一行并传送给取整数操作, 后者求出这一行代表的整数值并返回. 而下面的例子 sort( <stdin>) 排序操作为 <STDIN> 提供了一个列表上下文, 从 STDIN 中读入所有的行直到文件结束, 这些行组成的列表被传送到排序操作, 后者对它们排序并返回排序后的行列表. 赋值操作是用等号左边的参数来确定右边参数的上下文. 为数值变量赋值的操作给右边参数确定了数值上下文, 给数组或数组的一部分赋值的时候右边参数是处于列表上下文. 自定义的子过程可以自行确定被调用时的上下文, 但很多场合下不需要这么做, 因为数值可以自动转化成列表. 参看 wantarray . 数值变量 数值型不必确定自己的类型. 其实也没有地方去把一个数值型变量声明为``string'', 或 ``number'', 或``filehandle'', 或是其它什么类型. 在 Perl 中, 数值型变量的类型可以是数字或字符串或引用, 根据上下文确定. 字符串和数字实际上没什么两样, 但引用是不可转换的指针, 有内建的引用计数和析构过程. 非空的字符串或非 0 (字符串``0'')的数字可以表示布尔类型中的真值. 布尔上下文是一种特殊的数值型上下文. 空数值型实际上有两种情况: 已定义和未定义的. 当没有任何实际的值存在时, 未定义的空数值型被返回, 比如发生错误的时候, 或者读到文件结束的地方, 或者引用了一个未初始化的变量. 当一个未定义的空数值型首次被使用时, 就变成已定义的, 在此之前可以用 defined() 去检查一个值是否被定义了. 要知道一个字符串是否一个有效的非 0 数字, 一般是确定它不是数值 0 或 字符串 ``0'' if ($str == 0 &amp;&amp; $str ne &quot;0&quot;) { warn &quot;That doesn't look like a number&quot;; } 别的方法是用正则表达式进行检查, 参看 perlre 中对正则表达式的详细介绍. warn &quot;has nondigits&quot; if /\D/; warn &quot;not a whole number&quot; unless /^\d+$/; warn &quot;not an integer&quot; unless /^[+-]?\d+$/ warn &quot;not a decimal number&quot; unless /^[+-]?\d+\.?\d*$/ warn &quot;not a C float&quot; unless /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/; 数组的长度是一个数值型. 数组 @days 的长度是 $# days, 的值, 和 csh 里一样. (实际上这不是数组的长度, 是最大的下标, 因为有数组里第 0 个元素.) 给 $# days 赋值可以改变数组的长度. 用同样的方法缩短数组的长度会破坏被截去的数值. 重新增加被缩短的数组不再能恢复这些数值(在 Perl 4 里是可以的, 但为了保证正确的调用析构做了这样的改变). 给超出最大下标的数组元素赋值可以扩展数组的长度, 给数组赋上空的列表 () 就把数组的长度减为 0 . @whatever = (); $#whatever = $[ - 1; 如果在数值型上下文里计算数组的值, 会得到数组的长度. (并不适用于列表, 会返回最后一个值, 象 C 的逗号操作符) 以下的等式是成立的: scalar(@whatever) == $#whatever - $[ + 1; Perl 第 5 版改变了 $[ 的语意: 不设置 $[ 的文件不用担心别的文件会否改变自己的 $[ 的值. (换句话说, 不赞成使用 $[) 所以一般可以认为 scalar(@whatever) == $#whatever + 1; 有些程序员选择显式的转换: $element_count = scalar(@whatever); 如果在数值型上下文里计算哈希表的值, 如果哈希表里有key/value对, 会得到一字符串, 其中包含了该哈希表占据的空间和已经分配的空间, 用 '/' 分开. 这可以检查一下 Perl 的散列算法是否有效. 比如, 在哈希表里存放了 10,000 个元素, 而在数值型上下文里计算 %HASH 得到 ``1/16'', 那情况就太浪费空间了. 数值型的值的表示 12345 12345.67 .23E-10 0xffff # 16 进制 0377 # 8 进制 4_294_967_296 # 用下划线容易阅读 字符串一般用单引号或双引号括住. 引号的作用和 shell 里类似: 双引号字符串中可以有反斜杠和变量替换; 单引号字符串不行(除了 ``\''' 和 ``\\''). 一般的 Unix 反斜杠替换规则同样用来表示换行符, 制表符...等等. 见列表qq. 换行符可以直接嵌入在字符串中. 但如果忘记了结尾的引号, 直到 Perl 找到另外一行有引号的行前不会报出任何错误. 字符串中的变量替换限制于数值型, 数组和数组的部分. (即以$ 或 @ 打头的标识符, 后跟一个可选的括起来的下标) 下面的代码打印出 "The price is $100." $Price = '$100'; # not interpreted print &quot;The price is $Price.\n&quot;; # interpreted 和在某些 shell 下一样, 可以用花括号把标识符括住, 以区别于其他后随的字母. 实际上, 花括号中的标识符一定是个字符串, 就象一个哈希表的下标. 早先的例子 $days{'Feb'} 可以写成 $days{Feb} 引号是会被自动加上, 而下标中复杂的部分会被解释为表达式. 要注意, 单引号字符串要和前面的词用空白隔开, 因为单引号本身是可以组成标识符. (参看Packages). 有两个特别的字符串是 __LINE__ 和 __FILE__, 分别代表程序执行点的当前行号和文件名. 它们只能用做分隔记号; 不能被转换成字符串. 此外, __END__ 可以用于在脚本的真正结束位置前标记逻辑结束位置, 在此后的所有文字都被忽略, 但可以通过 DATA 文件句柄读出. (DATA 文件句柄只能从主脚本读取, 不能从required包含的文件或计算的字符串读取) 控制字符 ^D 和 ^Z 是 __END__ 的同义字. (在模块里是 __DATA__ 的同义字; 关于 __DATA__ 的详细说明, 参看SelfLoader) 在语法上没有其他解释的词都被看作一个引起来的字符串. 称为``净词''. 和文件句柄和标号一样, 全是小写字母的净词可能会和将来的保留字冲突, 如果使用 -w 选项, Perl 会对这些词发出警告. 有些人会完全不使用净词. 如果用 use strict 'subs'; 那么所有不能被解释为子过程调用的净词都会产生一条编译时刻错误. 严格检查一直到闭合块的结尾为止. 内部块用以下方法可以撤消严格检查 no strict 'subs' . 如果把数组的所有元素连接到一起形成一个双引号字符串, 以变量 $" ( $LIST_SEPARATOR )指定的字符做为分隔符(默认为空格). $temp = join($&quot;,@ARGV); system &quot;echo $temp&quot;; system &quot;echo @ARGV&quot;; 在搜索模式(也要做替换)中模糊是很糟糕的: 到底 /$foo[bar]/ 是解释为 /${foo}[bar]/ ([bar]/ 是正则表达式字符类) 还是解释成 /${foo[bar]}/ ([bar]/ 是数组下标)? 如果 @foo 不存在, 那么明显是个字符类. 如果 @foo 存在, Perl 要猜测 [bar] 是什么, 一般结果是正确的. 但如果猜错了, 或者你可以用花括号来指示正确的解释方法. 面向行的引用是建立在 shell 的 ``here-doc'' 语法上. 在 << 后面指定一个结束引用的字符串, 当前行的所有后随行直到结束串都是引用的内容. 结束字符串可以是一个标识符(一个词), 或引起来的一段文字. 如果是引起来的文字, 引的方式决定了对文字的处理, 象普通的引用. << 和标识符之间不能够有空白. 如果有空白, 引用到第一个空白行为止. 结束字符串必须单独出现在一行, 前后不能有空白. print <<EOF; # same as above 要记得结束的分号, 下面的代码是错误的 print <<ABC 列表值的表示 (LIST) 在非列表的上下文里, 列表的值是最后一个元素的值. 例如, @foo = ('cc', '-E', $bar); 把整个列表的值赋给数组foo, 而 $foo = ('cc', '-E', $bar); 把变量bar的值赋给变量foo. 注意在数值型的上下文里数组的值是数组的长度; 下面的赋值把 $foo 的值设为 3: @foo = ('cc', '-E', $bar); $foo = @foo; # $foo gets 3 列表的结束括号前可以有一个逗号: @foo = ( 1, 2, 3, ); 列表会把子列表自动展开. 当列表被求值时, 其中的每个元素都在列表上下文里被展开, 最后的结果形成一张大的列表. 数组在列表里不再独立存在. 下面的列表 (@foo,@bar,&amp;SomeSub) 包含了 @foo 中的所有元素, 后面是 @bar 的所有元素, 再后面是子过程 SomeSub 在列表上下文里被调用时返回的所有元素. 要使用不展开列表的引用, 参看 perlref 空列表用 () 表示. 在列表中展开一个空列表是不产生任何变化. 因此 ((),(),()) 等同于 (). 类似的, 在列表中展开一个空数组也不产生任何变化. 可以把列表当作数组, 用下标去访问它的值. 但必须用括号括起列表以避免产生混乱. 例如: # Stat 返回列表值 $time = (stat($file))[8]; # 语法错误 $time = stat($file)[8]; # 漏了括号 # Find a hex digit. $hexdigit = ('a','b','c','d','e','f')[$digit-10]; # A &quot;reverse comma operator&quot;. return (pop(@foo),pop(@foo))[0]; Lists may be assigned to if and only if each element of the list is legal to assign to: 只有当可以对列表中的每个元素赋值, 才可以对列表赋值. ($a, $b, $c) = (1, 2, 3); ($map{'red'}, $map{'blue'}, $map{'green'}) = (0x00f, 0x0f0, 0xf00); 在数值型上下文里, 列表的赋值操作的返回值是等号右边的表达式的值. $x = (($foo,$bar) = (3,2,1)); # $x = 3, 不是 2 $x = (($foo,$bar) = f()); # $x 等于 f() 的返回值 当在布尔型的上下文里进行列表赋值操作时, 检查返回值是有意义的. 大部分列表函数结束的时候返回空列表, 即数值 0, 布尔值 FALSE. 待赋值列表的最后一个元素可以是哈希表: ($a, $b, @rest) = split; local($a, $b, %rest) = @_; 实际上, 赋值的时候可以把哈希表放在列表的任何位置, 但列表中第一个出现的哈希表会取去所有的值, 后面的元素都得到一个空值. 这个特点在 local() 或 my() 里可能会有用. 哈希表的元素是一对对的数值, 即键和值的对应. # same as map assignment above %map = ('red',0x00f,'blue',0x0f0,'green',0xf00); 列表和数组之间通常是可以互相转换的, 但不适用于哈希表. 列表中的元素可以象数组一样用下标去访问, 但不能用键去访问列表元素. 在哈希表的键和值之间使用 => 操作符会更加明白. => 操作符是逗号的同意词, 但兼有引起左操作数的功能, 特别适用于哈希表的初始化. %map = ( red =&gt; 0x00f, blue =&gt; 0x0f0, green =&gt; 0xf00, ); 或者是初始化哈希表的引用, 当作记录使用. $rec = { witch =&gt; 'Mable the Merciless', cat =&gt; 'Fluffy the Ferocious', date =&gt; '10/31/1776', }; 或是用于按名调用的复杂函数: $field = $query-&gt;radio_group( name =&gt; 'group_name', values =&gt; ['eenie','meenie','minie'], default =&gt; 'meenie', linebreak =&gt; 'true', labels =&gt; \%labels ); 要记住哈希表的初始化顺序不等于数据的存放顺序. 参看 sort 中有关对输出进行排序的例子 Typeglobs 和 文件句柄 有一个地方还会用到 typeglobs, 就是传递或存放文件句柄的时候. 要保存一个文件句柄, 可以这样做: $fh = *STDOUT; 或者用真正的引用: $fh = \*STDOUT; 这也是建立局部文件句柄的方法. 例如: sub newopen { my $path = shift; local *FH; # 不用 my! open (FH, $path) || return undef; return \*FH; } $fh = newopen('/etc/passwd'); 欲知更多有关 typeglobs 的信息, 参看 perlref , perlsub , 和 ``Symbols Tables'' . 欲知其他生成文件句柄的方法, 参看 open . |
请发表评论