Perl编程备忘

这个“备忘”中所有的中文名词与flamephoenix的Perl 5教程一致。对于这些基本名词我都不加解释,有疑问请到这里查询

1. 关联数组和复杂数据

Perl不支持“结构”和“类”,然而它还是有办法实现复杂的数据结构的,工具就是“关联数组(Hash)”和“引用(reference)”。关联数组是一个以索引(key)来取得值(value)的对照表,然而当我们把关联数组的值定义成一个引用的时候,关联数组就有了更多的灵活性。引用在C++里是一个已经存在的概念,Perl里的引用更类似于指针,它能够指向任何的数据结构

假设一个学校按以下结构处理它的学生成绩单数据,学校下面是以班号为索引的班级,班级下面是以学号为索引的学生。学生有名字生日等一些信息。每个学生选修的课程不一样,以课程名字为索引,下面是课程成绩。是如下的一种

%hashSchool->$nClassNo->$nStdNo->'Name'=$szStdName
%hashSchool->$nClassNo->$nStdNo->'Birth'='2079-9-15'
%hashSchool->$nClassNo->$nStdNo->'courses'->'Quatum Mechanics'='A'
%hashSchool->$nClassNo->$nStdNo->'courses'->'History of Art'='B'

那么一个给定学生名字和生日,要求打印他的所有成绩单的Perl子程序如下(假设%hashSchool已经有了内容):

using Data::Dumper;

$szStdName = "Sebastien Ouyang";
$szStdBirth = '2090-3-22';
&PrintTranscript( \%hashSchool, $szStdName,$szStdBirth);

sub PrintTranscript{
    my ($rHashSchool, $szStdNameQuery, $szStdBirth) = @_;
    
    while( my($nClassNo, $rClass) = each %{$rHashSchool}){
    while( my($nStdNo, $rStd) = each %{$rClass}{
    if( ($rStd->{'name'} eq $szStdNameQuery) and ($rStd->{'birth'} eq $szStdBirth)){
        print Dumper( $rStd);
}
}
}
}
为了简化只找出第一个匹配的学生,当然实际中得找出所有匹配的学生放到一个列表再做处理。警告:在遍历关联数组的过程中最好(只是说最好,其实有些情况还是需要的)不要对它增加或者删除任何元素,否则会产生预想不到的效果。

2. 文件句柄
Perl的变量名命名方法是类似Basic的,用前置的符号%, @, $等来表示变量类型。但是有一个例外,就是文件句柄,它可以没有前置符号。但是这样就有个问题,怎么定义它的作用范围是my, local还是global呢?如果全都是global,没准就会产生冲突。可以使用FileHandle包
use FileHandle;
my $FH = new FileHandle("file");
$FH->close();
上面句子的作用和下面相同
open(FH, "file");
close(FH);
取文件的一行的用法
$line = <FH>;
$line = $FH getline();

3. 可变长参数列表
在C++里,用“重载”的方式定义了对同一个函数名,不同参数列表的函数的调用方式。如果你能深入到C++生成的link符号文件,会发现这些函数会生成不同的函数体。在这一点山Perl拥有优势,它各每个函数生成参数栈列表@_,对参数栈还有灵活的shift/pop操作。

举例子来说,你需要一个Max函数找出一堆数里面的最大值,但是你不确定会有多少个数。
$nMax = &Max(1, 2, 9);
$nMax = &Max(1.0, 2.9, 9, 3.0e3);

sub Max{
my(@values) = @_;
my $nMax = $values[0];
foreach $value (@values){
    $nMax = $value if( $value > $nMax);
}
或者另外一种写法
sub Max{
my $nMax = shift;
while( @values){
    $value = shift;
    $nMax = $value if( $value > $nMax);
}
}
@_是本地的(my), 所以perl的函数支持递归调用。另外一个要注意的是,Perl从来不复制传递给子函数的参数,也就是说,Perl没有“按值传递”的说法。所以在子函数里改变@_[0]的值,上一级函数里对应的调用参数就真的改变值了。

4. 正则表达式的一些特性
4.1 匹配多行
/s选项可以对一个内部包含换行符号的字符串进行匹配。例如
$string = "This is a\n mutiple line string.";
$string =~ /This.*strin/s;
不用/s会导致'.'不匹配换行,下面的匹配会失败。
$string =~ /This.*strin/;
4.2 多重匹配
通常正则表达式第一次找到匹配的字符串就返回。用/g选项和while循环配合可以多次匹配。假设一个C语言文件已经整个被读入@line,我们要打印出里面所有的用/*和*/包含的注释。当然这些注释是可以跨行的,所以也要用到上面的/s选项。
my $lines = '';
foreach $string (@line){
    $lines .= $string;
}
while( $lines =~ /\/\*(.*?)\*\//gs){
    print $1;
}
一定要注意,在while循环中不可以改变$lines的值,否则有陷入死循环的危险。如果一定要改变,用另外一个变量。
4.3 非
^在正则表达式中被用来作为“行首”要求。但是它还有一个功能,加在某个字母前表示“非”。例如
$string = "a*b*c*def";
$string =~ /[^*]+/;
表示匹配非*的任意长字符串,第一次会得到'a'.
4.4 替换时的必须品quotemeta()
在正则表达式里,特殊字符前必须加一个反斜杠'\'。如何判断一个字符串里有多少特殊字符要做这个处理?Perl提供了函数quotemeta()来做这个事情。
$string = '+-*/&^%';
$substr = '+-*';
$substr = quotemeta( $substr);
$newsub = 'Add/Sub/Mult';
$string =~ s/$substr/$newsub/;
注意新内容$newsub一定不要用quotemeta处理。

5. 结构化编程
在C语言的结构化编程方式中,常常会把属于相同类型的函数(比如说,文件处理,或者音频处理)放到同一个.c文件中。Perl也可以这样。通常会把一个项目分成一个.pl文件和若干个.ipl(也可以用别的后缀名,但这个是最流行的)文件。ipl文件里面放一些子函数,它的写法和.pl文件并没有本质区别,但是要在文件头部加上package __ABC__;(这里假设你定义自己的模块的名字叫ABC);在文件最后加上一行字1;表示有缺省的返回值。

在pl文件里要这样调用ipl里面的子函数:首先要在文件头部宣称require "abc.ipl";(假设ipl文件名就是abc.ipl),然后在文件里就可以用 __ABC__::abcdef()来调用abc.ipl里面的子函数abcdef()了。

6. 模块与名字空间
上面说的require是Perl的一个语句,用户可以这样
if( $szInput = 'a'){
    require 'a.ipl';
}else{
    require 'b.ipl';
}
在Perl程序运行到这个if语句以后,才能决定载入哪个ipl文件。这样很灵活,但是也很糟糕,因为有可能在运行到这句程序已经执行了一个小时,然后载入b.ipl,到了这时候发现b.ipl中有语法错误,这样就浪费了一个小时的机时。所以最新的潮流是鼓励使用use,而不是require. use不能像一个语句一样使用,只能放在文件头部,在编译时所包含的模块文件就会被检查。

生成一个use模块需要做以下事情:
(1) 生成一个后缀.pm的文件,例如abc.pm。在.pm文件的头部必须有package abc;--在这里模块的文件名等于模块的包名,模块的包名等于模块的名字空间名。模块的名字可以是这样package AAA::abc, 这样可以方便把一些模块归类到A。
(2) 要使用模块,在程序里声明use abc; 之后就可以abc::xyz()来调用abc里的函数xyz.
(3) 可以在.pm文件中包含一个import()函数,它将在模块被use的时候自动执行。注意use是可以带参数的,这个参数就传给了import(). 例如
use abc 2.31;
在.pm里
package abc;
my $_actualversion = 2.50; # this modoule version is 2.50
sub import{
    shift;    #注意第一个参数是包名'abc'
    my $version = shift;
    if( $version > $_actualversion){
        die "You require version $version and this is $_actualversion\n";
}
}
1;
以上代码被普遍用于对模块的版本做检查。

7. 在使用use strict时使用全局变量
以下代码是不对的
use strict;
$string = 'a';
需要这样
use strict;
use vars qw($string);
$string = 'a';

8. %INC和@INC
C程序员一定很熟悉以下代码
#ifndef _HEADER_A_
#define _HEADER_A_
#include "header.h"
......
#endif
以上代码是为了防止一个头文件被多次包含。Perl要聪明一些,它会把所有已经调用的模块保存在%INC中,调入新的模块前会先查看一下是不是已经调入了。另外,对于特别大的工程,模块有重名冲突的危险,这时可以打印出这个关联数组:
use abc;
while( my($key, $value) = each %INC){
    print "$key => $value\n";
}

将会打印出
abc => /usr/local/lib/myperl/abc.pm

@INC存储的是在Perl中可用的路径。有4种设置方式
(1) $ export PERL5LIB="my_path"
(2) use lib "my_path" 推荐这种方式
(3) unshift(@INC, "my_path");
(4) 修改Perl的config.sh文件

你可能感兴趣的:(perl)