Perl学习手记

[TOC]

Perl’s philosophy

There is more than one way to do it. No unnecessary limits.

文件操作

  • open LOG,"< filename" 打开文件句柄
  • print FILE $out; 向文件句柄写
  • while(<LOG>) 使用文件句柄
  • select LOG 选择文件句柄
  • $| 用来清空文件句柄
  • <> diamond操作符,实现类UNIX工具方式。不需要使用open,默认逐行操作一 个文件。比如 while(<>){ print; } 就是 cat 的效果。

文件检测

  • -e $filename 返回布尔值,判断文件是否存在
  • -M FILEHANDLE 返回天数,该文件最后修改时间
  • -s $filename 返回文件大小,以Byte为单位
  • -A $filename 返回天数,该文件最后访问时间 … 见《Learning Perl》140页表

特殊符号

  • $_ 隐式标量……
    • $. 行号
    • @_ 列表
    • $_{_} Hash值
  • @ARGV 包含参数的列表
  • $^I 对<>操作符,如果没有指定文件名,则其从标准输入流中自动打开和关闭一系列文件进行读入。但如果$^I 中有字符串,这个字符串则会成为备份文件的扩展名
  • $! 系统错误信息
  • %ENV 环境变量表,比如 $ENV{'PATH'}, $ENV{'PWD'}
  • $@ 当使用 eval{}; 结构时,保存程序失败原因

符号?的用法

  • 在原来的操作符后加?,表示尽可能少的匹配而不是尽可能多的匹配,比如 +?,*?,{2,10}? 等等,它在解析html块的时候很有用
  • 非捕捉用法,比如 /(?:ronto)?saurus/,此时的ronto只是用来分组,而不会添加到变量$1中

注意事项

  • 使用@来引用list
  • 使用%来引用hash,比如hash反转 %A = reverse %A
  • 读取list元素写作 $list[1],如果写作 @list[1] 将取出一个列表,这个 列表中包含对应该索引的一个元素,同理,读取列表中的一组元素 @list[1,4],称作 array slice,它的效果跟 ($list[1],$list[2]) 是一样的。
  • 读取hash元素写作 $hash{$key},hash同样有 hash slice,比如 @hash{qw/key1 key2/},得到的同样是slice列表
  • 直接用 ($item1,$item2) 表示列表,常用于赋值,比如slice用法 my(undef, $item1, undef, $item2) = split /:/; 其中undef表示不关心的内容。
  • 直接利用索引来引用列表,比如 my $myitem = (stat $filename)[9],则上边 可以表示为 my($item1,$item2) = (split /:/)[1,-1],其中 -1 表示列表中 最后一个元素。

regex

  • \d [0-9]
  • \w [A-Za-z0-9_]
  • \s [\f\t\n\r]
  • \D [^\d]
  • \W [^\w]
  • \S [^\s]
  • 模式匹配 m// 可以省略m,或者 m{}
  • //i 忽略大小写
  • //s 使.匹配换行符
  • /\bwords\b/ 单词锚定,如果写作 /words\b/ 表示锚定单词结尾
  • Perl的变量作用域,比如 模式匹配 之后的$1,$2,或者$_@_ 等等

    例如, 一个把C语言定义转化成PHP定义的模式匹配:

    open FILE, "< filename";
    while(<FILE>){
        if(/define (STAT_\w+) (\d+)(.*)/){
            print "define('$1', $2); $3";
        }
    }
    
  • $& 匹配中的整个字符串
  • $ 匹配成功前的那一部分
  • $' 还没有匹配的剩余部分(使用以上三个变量会降低效率,不如替换成$1,$2这些)
  • s/// 匹配后替换(一次),例如替换 $line 中的 $pattern$replace$line =~ s/$pattern/$replace/
  • /g 全局替换
  • /m 辨认内部换行符

字符串限定符号

  • \U\L可以强制该变量内的字符大写或者小写,比如 “\L$a

常用函数

  • split ":", $string 类似php的explode函数 @field = split /limiter/, $string;
  • join ":", @array 和split相反,类型php中的implode函数
  • glob "*.bak" 选取所有的以.bak结尾的文件到一个列表

字符串操作

  • index($string, $pattern) 类似C中的strchr或者PHP中的strstr不过这里可以 匹配字符串,而非单字符。逆方向的匹配为 rindex
  • substr($string, $begin, $length) 类似PHP中有同样的函数。不过Perl中的比 较神奇,可以当作左值进行替换操作。比如 substr($string,0,5) = $replace,则 $replace 将会替换掉 $string 中的前5个字符,$replace长度不 限。这段的传统写法是使用第四个参数 substr($string,0,5,$replace)。当 然,这些事情用regexp也同样可以搞定。

排序

  • 三向比较符 <=>cmp,前者返回三种值,后者返回两种值
  • sort by_number @array 不过常写作内联函数 sort {$a<=>$b} @array。值得 注意的是 sort 不能对hash进行排序,它只是对hash的keys或者values进行排 序,比如 sort by_number keys %hash

对列表的操作

  • grep {} @array 比如找出文件中匹配的行返回一个新的列表 grep {/\bpattern\b/} <FILE>,它的作用是和UNIX命令grep一致的。
  • map {} @array 对列表进行转换,重新输出

获取当前时间

使用localtimetime函数,例如:

my ($sec,$min,$hour,$day,$mon,$year,$weekday,$yeardate,$savinglightday) = (localtime(time));
my $timstr =  sprintf("%04d-%02d-%02d %02d:%02d:%02d",$year+1900,$mon+1,$day,$hour,$min,$sec);

控制结构

  • forforeach在Perl中是等价的,Perl根据其后的括号来判断循环行为
  • 善于利用or die书写良好的风格:do this or die

模块

  • 一个模块的例子,取名为Personal.pm

    package Personal;
    sub adv {
        my @input = @_;
        my $total;
        $total+=$_ for (@input);
        $adv = $total/scalar(@input);
    }
    1;
    
  • 使用该模块

    use strict;
    use Personal;
    my @grades = (67, 73, 57, 44, 82, 79, 67, 88, 95, 70);
    my $adv = Personal::adv(@grades);
    print $adv."\n";
    

引用

  • 获取list引用的变量 @{$list_ref}/${$list_ref}[1]
  • 获取Hash引用的变量 %{$hash_ref}/${$hash_ref}{key}
  • 匿名引用的作用在于可以将标量、列表和Hash都转化成标量来存贮,这样可以构 造更加有弹性的数据。比如

    my @john_grades = (65, 87, 92, 77, 53);
    my %john = ( id => '7821434',
        birth => '1983/11/12',
        grades => \@john_grades );
    ...
    my %students = ( john => \%john,
        ...);
    
  • 通过引用,可以获得跟php中的array一样有弹性的数组,这次不需要把那些变 量表露出来。其中array使用中括号[],hash使用花括号{}。

    #!/usr/bin/perl
    use strict;
    my %students = (
        john => {   id => 'foo',
                            tel => '11223344',
                            grades => [34, 56, 78]},
        paul => {    id => 'bar',
                            tel => '223344',
                            grades => [44, 55, 66]},
            );
    

一些注意事项:

  1. 当想传递给子程序的参数是多于一个的数组时 一定要 使用引用。
  2. 一定不要 在子程序中使用形如 (@variable)=@_; 的语句处理参数,除非你想把 所有参数集中到一个长的数组中。

代码重用 的经典案例.

sub new {
        my $class = shift; # Get the request class name
        my $this = {};
        bless $this, $class # Use class name to bless() reference
        $this->doInitialization(); return $this;
}

可以多次bless一个引用对象,然而,新的将被bless的类必然把对象已被bless的引用去掉,对C和Pascal程序员来说,这就象把一个指针赋给分配的一块内存,再把同一指针赋给另一块内存而不释放掉前一块内存。总之,一个Perl对象每一时刻 只能 属于一个类。 对象和引用的真正区别是什么呢?Perl对象被bless以属于某类,引用则不然,如果引用被bless,它将属于一个类,也便成了对象。对象知道自己属于哪个类,引用则不属于任何类。

奇淫技巧

flip-flop结构

打印文本中所有夹在begin行和end行之间的内容:

while (<>) { print if /^begin/../^end/ }

一般有两种用法:

  • /regexp1/../regexp2/ 实际相当于($_ =~ /regexp1/)..($_ =~ /regexp2/),以满足regexp1为入口条件,满足regexp2为出口条件
  • lineno1..lineno2 实际相当于($.==lineno1)..($.==lineno2),这个表示特定行,print if 5..12表示打印文件的第5行到第12行

心得

Perl有十分多的在线文档,可以用perldoc获得,比如

  • perldoc -f func_name 用来获取函数func_name的说明
  • perldoc perlreftu 获取引用教程
  • perldoc perlfaq4 Perl的FAQ里边有好多非常棒的使用经验

map

从赋值来看,List和Hash的差别只是在于list是数组方式分赋值,而Hash是数对方式的赋值,因此常常把List转化到一个Hash可以这样:

%hash = map { $_, 1} @list

而在map看来,它所能理解的只是一个列表,所返回也是一个列表:

@an_list = map { $_ } @list

引用

当函数返回一个引用之后,其引用变量的作用域范围多大?


原先记录在Google Pages上。