Learning Perl总结提升

来源:互联网 发布:百度推广软件 编辑:程序博客网 时间:2024/05/18 00:42

第一章简介

第二章表量数据

第三章列表与数组

第四章子程序

第五章输入与输出

文件句柄就是程序里代表进程(process)与外界之间从输入输出联系的名称。

1写一个功能跟cat相似的程序,但将各行内容反序后输出.

Print reverse<>

2写一个程序,要求用户分行键入各个字符串,然后以20个字符宽、向右对齐的方式输出每个字符串。为了确定输出结果在适当的字段中,请一并输出由数字组成的“标尺行”。

print “Enter somelines,then press Ctrl-D:\n”;

chomp(my @lines=<STDIN>);


print“1234567890”×7,“12345\n”;#标尺行,到第75个字符的地方


foreach(@lines){

printf“%20s\n”,$_;#交由%20s转换后输出

}

3修改上一个程序,让用户自行选择字符宽度,因此在键入30的时候,hellogood-bye(在不同行上)应该会向右对齐到第30个字符。根据用户键入的宽度,自动调整标尺行的宽度。

print“What columwitdth would you like?”;

chomp(my $width=<STDIN>);


print “Enter somelines,then press Ctrl-D:\n”;#长度按需变化的标尺行


foreach(@lines){

printf“%${width}s\n”,$_;

}

第六章哈希

1编程读入用户指定的名字并且汇报相应的姓。

my %last_name =qw{

fred flinstone

barney rubble

wilma flintstone

};

print “Pleaseenter a first name:”;

chomp(my $name=<STDIN>);

print “That's$name $last_name{$name}.\n”;


2编程读取一系列单词,每行一个,指导文件中止,然后打印一份列出每个单词出现次数的列表。

my(@words,%count,$word);#申明变量

chomp(@words=<STDIN>);


foreach $word(@words){

$count{$word}+=1;

}


foreach $word (keys%count){

print word was seen$count{$word}times.\n”;

}

3 编程输出%ENV哈希中所有的键值对,输出按照ASCII编码排序,分两列打印。设法让打印结果纵向对齐。注意length函数可以帮助确定第一列的宽度。测试完毕后加入更多新变量再次验证程序的输出正确无误。

My $longest =0;

foreach my $key(keys %ENV){

$longest=$key_length if $key_length>$longest;

}

foreach my $key(sort keys %ENV){#依次处理hash,对键值进行排序

printf“%-${longest}s %s\n”,$key,$key,$ENV{key};

}


第七章漫游正则表达

1从输入中读取数据,遇到包含fred字符串的行就打印出该行。

While(<>){

if (/fred/){

print;

}

}

6写程序,输出在输入数据中同时出现wilma以及fred的每一行

while(<>){

if(/wilma/){

if(/fred/){

print;

}

}

}


第八章用正则表达式进行匹配

1 利用模式测试程序,写个模式,使它能匹配match这个字符串。

befor<match>after

2 利用模式测序程序,写个模式,使其能够匹配任何以a结尾的单词(以\w组成的单词)。

/a\b/

3修改上题程序,使其在匹配到以a结尾的单词的同时叶将其存储在$1里。

#/usr/bin/perl

while(<STDIN>){

chomp;

if(/(\b\w*a\b)/){#\b单词边界锚位

print“Matched:|$`<$&>$`|\n”;

print “\$1contains '$1'\n”;

}else{

print “Nomatch:|$_|\n”;

}

}

4使用命名捕获而不是$1这样的老办法。接着修改程序输出,让标签名字出现在结果中,例如:‘word'contains 'Wilma'

#/usr/bin/perl

use 5.0.10;

while (<STDIN>){

homp;

if(/(?<word>\b\w*a\b)/){

print “Matched:|$`<$&>$`|\n”;

print “'word'contains '$+{word}' \n”;#新的输出行

}else{

print “No match:|$_|\n”;

}

}

6写个新程序,输出其输入中以空白结尾的行。输出的时候,在行尾多加一些记号,这样比较容易看出空白符。

While(<>){

chomp;

if (/\s\z/){

print “$_#\n”;

}

} 5f 5tvftfc

第九章用正则表达式处理文本

2复制并修改指定的文本文件。在副本里,此程序会出现字符串Fred的每一处都换成Larry。输入文件名应该在命名行上指定,输出文件名则是本来的文件名加上.out

my $in=$ARGV[0];

if (! defined $in){

die “Usage:$0filename”;


my $out =$in;

$out =~s/(\.\w+)?$/.out/;


if (! open$in_fh,'<',$in){

die “Can't open'$in':$!”;

}


if (! open$out_fh,'>',$out){

die “Can't write'$out':$!”;

}


if (!open$out_fh,'>',$out){

die “Can't write'$out':$!”;

}


while (<$in_fh>){

s/Fred/Larry/gi;

print $out_fh $_;

}


3 把所有Fred换成Wilma,并把所有Wilma换成Fred.

While (<$in_fh>){

chomp;

s/Fred/\n/gi;

s/Wilma/Fred/gi;

s/\n/Wilma/g;

print $out_fh“$_\n”;

}

4 前面所有程序都加上版权申明

$^I = “.bak”;#制作备份

while(<>){

if(/\A#!/){##!开头的那行吗?

$_.=”##Copyright C)20xx by Yours Truly\n;

}

print;

}

第十章其他控制结构

1让用户不断猜测范围从1100的秘密数字,直到猜中为止。程序应该以int(1+rand100)来随机产生秘密数字。当用户猜错时,程序应该回应”Toohight”或“Toolow”.如果用户键入quitexit等字样,或是键入一个空白行,程序就中止。用户猜到了,也中止。

My $secret=int(1+rand 100);

print “

Don't tellanyone,but the secret number is $secret.\n”;

while(1){

print “Pleaseenter a guess from 1 to 100:”;

chomp(my $guess=<STDIN>);

if ($guess =~/quit|exit|\A\s*\z/i){

print “Sorry yougave up.The number was $secret.\n”;

last;

}

elsif($guess<$secret){

print”Too small.Try again!\n”;

}elsif ($guess==$secret){

print “That wasit!\n”;

last;

}else “Toolarge.Try again|\n”;

}

}

第十一章Perl模块

2 写一个程序,用DateTime模块计算当前日期和输入日期之间的间隔。输入日期时,在命令行依次输入表示年月日的数字;

CPAN安装DateTime模块后,只需按要求创建两个日期对象,然后两者相减

use DateTime;

my $t= localtime;

my $now=DateTime->new(

year =>$t[5]+1900,

month =>$t[4]+1,

day =>$t[3],

);


my $then =DateTime->new(

year =>$ARGV[0],

month=>$ARGV[1],

day =>$ARGV[2],

);


my $duration =$now-$then;

my @units =$duration->in_units(qw(years months days));

printf “%dyears,%d months,and %d day\n”,@units;


if ($now<$them){

die “You entereda date in the future!\n”;

}

第十二章文件测试


1写一个程序,从命名行取得一串文件名,并汇报这些文件是否可读、可写、可执行以及是否确实存在。

Foreach my $file(@ARGV){

my $attribs=&attributes($file);

print “'$file'$attribs.\n”;

}

sub attributes {

my $file =shift @_;

return “does notexist” unless -e $file;


my @attrib;

push @attrib,“readable” if -r $file;

push @attrib,“writable” if -w $file;

push @attrib,“executable” if -x $file;return “exists” unless @attrib;

'is'.join“and”,@attrib;

}


2写一个程序,从命令行参数指定的文件中找出最旧的文件并且以天数汇报它已存在多久。

die “No file namesupplied!\n” unless @ARGV;#开始检测文件名

my $oldest_name =shift @ARGV;#第一个文件当然是目前唯一见过的文件中最旧的

my $oldest_age =-M$oldest_name;#记下年龄,存储在$oldest_age变量里


foreach (@ARGV){

my $age=-M;#M文件测试来取得它们的年龄,用$_的默认参数。将M返回的年龄存进临时变量$age里。

($oldest_name,$oldest_age)=($_,$age)

if $age>$oldest_age;

}

printf “The oldestfile was %s,and it was %.1f days old.\n”,

$oldest_name,$oldest_age;


第十三章目录操作

1写一个程序,让用户键入一个目录名称并从当前目录切换过去。如果用户键入一行空白符,则以用户主目录作为默认目录,所以应当会切换到他本人的主目录中。

print “Whichdirectory?(Default is your home directory)”;

chmop (my $dir=<STDIN>);

if ($dir =~/\A\s*\Z/){#如果名称不是空的,我们会切换到该目录下

chdir or die “Can'tchdir to your home directory :$!”;

}else{

chdir $dir or die“Can't chdir to '$dir':$!;

}


my @files=<*>;#使用星号的glob操作返回工作目录中所有的文件名,并自动按字母顺序配序,逐一输出

foreach (@files){

print “$_\n”;

}


2修改上题,输出所有文件,包括名称以点号开头的文件

my @files =<.**>;

3 改用打开目录句柄的方式

print 'Whichdirectory? (Default is your home directory)';

chomp(my $dir=<STDIN>);

if ($dir =~/\A\s*\Z/){

chdir or die “Can'tchdir to your home directory:

$!”;

}else{

chdir $dir or die“Can't chdir to '$dir':$!”;

}


opendir DOT, “.”or die “Can't chdir to '$dir':$!;

foreach (sortreaddir DOT){

#next if/\A\./;##如果跳过文件名以点好开头的文件

print “$_\n”;

}

5编写功能和mv类似的程序,将命令行的第一个参数重命名为第二个参数(不必实现mv的各种选项或任何额外的参数)

use File::Basename;

use File::Spec;


my($source,$dest)=@ARGV;

if (-d $dest){

my $basename=basename $source;

$dest=File::Spec->catfile($dest,$basename);

}


rename $source,$dest

or die “Can'trename '$source' to '$dest':$!\n”;

}


第十四章字符串与排序

1编写程序,读入一连串数字并将它们按数值大小排序,将结果以右对齐的格式输出。

my @number;

push @number,splitwhile <>;

foreach (sort {$a<=>$b} @numbers){

printf“%20g\n”,$_;#让它们靠右对齐

}

2以不区分大小写的字符顺序把下列的哈希数据按姓氏排序后输出。当姓一样时,再按名排序。

my @keys = sort{

“\L$last_name{$a}”cmp “\L$last_name{$b}”

or

“\L$a” cmp“\L$b”

}keys %last_name;

foreach (@keys){

print“$last_names{$_},$_\n”;#按排序打印

}

3在输入字符串中找出指定子字符串出现的位置并将其输出

print “Pleaseenter a string:”;

chomp(my $string=<STDIN>);

print “Pleaseenter a substring:”;

chomp(my $sub=<STDIN>);


my @places;

for (my $pos=-1;;){#三节式for循环的技巧性用法

$pos=index($string,$sub,$pos+1);#找出下个位置

last if $pos ==-1;

push @places,$pos;

}


print “Locationsof '$sub' in '$string'were:@places\n”;

第十五章智能匹配与given-when结构

1 given结构重写猜数字习题

use 5.010

my $Verbose=$ENV{VERBOSE}//1;

my $secret=int(1+rand 100);

print “Don't tellanyone,but the secret number is $secret.\n”

if $Verbose;

LOOP:{

print “Pleaseenter a guess from 1 to 100:”;

chomp(my $guess=<STDIN>);

my $found_it =0;


given($guess){

when(!/\A\d+\Z/){say“Not a number!”}

when($_>$secret){say“Too High!”}

when($_<$secret){say“Too low!”}

default {say “Justright!”;$found_it++}

}

last LOOP if$found_it;

redo LOOP;

}

4从命令行得到一个数字,打印出这个数字除了1和它本身以外的因数。

Use 5.010;

say “Cheaking thtnumber <$ARGV[0]>”;

given($ARGV[0]){

when(!/\A\d+\Z/){say “Not anumber!”}#前面的when通过尝试正则表达式匹配来判断我们确实有个数字,测试通过就能调用divisors()

my @divisors=divisors( $_);

my @empty;

when(@divisors~~@empty){say“Number is prime”}


default {say “$_isdivisible by @divisors”}

}


sub divisors{

my $number =shift;

my @divisors=();

foreach my $divisor(2..$number/2){

push@divisors,$divisor unless $number % $divisor;

}

return @divisors;

}


第十六章进程管理

2进入某个特定的目录,比如系统根目录。然后执行ls-l命令获得该目录内的长格式目录列表。

输出送到当前目录下的ls.out文件,错误输出则送到ls.err.

OpenSTDOUT,'>','ls.out' or die “Can't write to ls.out:$1”;

OpenSTDERR,'>','ls.err' or die “Can't write to ls.err:$!”;

chdir '/' or die“Can't chdir to root directory:$!”;

exec 'ls','-l' ordie “Can't exec ls:$!”;

3解析date命令的输出并判断今天是星期几。如果是工作日,输出getto work ,否则输出个goplay

if (`date` =~/\AS/){

print “goplay!\n”;

}else{

print “get towork!\n”;

}

4一个无线循环程序,它能捕获信号并报告之前受到过该信号的次数。如果收到INT信号就退出程序。如果可以在命令行使用kill命令,可以像下面这样发送特定信号。

Sue 5.0.10;


sub my_hup_handler{state $n;say 'Caught HUP:',++$n}

sub my_usr1_handler{state $n;say 'Caught USR1:',++$n}

sub my_usr2_handler{state $n;say 'Caught USR2:',++$n}

sub my_usr2_handler{say 'Caught INT. Exiting.';exit}


say “I am $$”;


foreach my $signal(qw(int hup usr1 usr2)){

$SIG{uc $signal} =“my_${signal}_handler”;

}


while(1) {sleep 1};

#打开另一个终端会话来运行程序以发送信号:

$kill -HUP 61203

$perl -e 'kill HUP=>61203'

$perl -e 'kill USR2=>61203'


#该程序的输出显示每次信号来到时我们已经见过的次数;

$ perlsignal_catcher

I am 61203

Caught HUP:1

Caught HUP:2

Caught USR2:1

Caught HUP:3

Caught USR2:2

Caught INT.Exiting.


第十七章高级Perl技巧

1从文件中读取一组字符串(每行一个),然后让用户键入模式以便进行匹配。

my $filename='path/to/sample_txt';

open my$fh,'<',$filename

or die “Can'topen '$filename':$!”;

chomp (my @strings=<FILE>);

while(1{

print 'Please entera pattern:';

chomp (my $pattern=<STDIN>);

last if $pattern=~/\A\s*\Z/;

#eval块用来捕获使用正则表达师可能发生的错误。grep会筛选出字符串列表中匹配模式的字符串

my @matches =eval {

grep/$pattern/,@strings;

};

if ($@){

print “Error:$@”;

}else{

my $count

2报告目录下文件的最后访问时间和最后修改时间。

#通过foreach把取得的每个文件名存到默认的控制变量$_中,因为stat默认也是使用这个变量

foreach (glob('*')){

my ($atime,$mtime)=(stat)[8,9];

printf “%-20s%10d %10d\n”,$_,$atime,$mtime;

}

3修改上题程序,把时间格式改为YYYY-MM-DD的形式。用map逐个输出,并用localtime通过列表切片提取纪元时间的年、月、日字段。

#localtime文档查到相应字段的索引位置

my @times =map{

my($year,$month,$day)=(localtime($_))[5,4,3];

$year +=1900;$month+=1;

sprintf'%4d-%02d-02d',$year,$month,$day;

}@epoch_times;

#替换之前程序中stat那行的代码,所以最终的结果

foreach my $file(glob('*')){

my ($atime,$mtime)=map{

my($year,$month,$day) =(localtime($_))[5,4,3];

$year +=1900;$month+=1;

sprintf'%4d-%02d-%02d',$year,$month,$day;

}(stat $file)[8,9];

printf “%-20s %10s%10s\n”,$file,$file,$atime,$mtime;

}



0 0