学习Perl(4)
散列
1)何谓散列
散列是一种数据结构,和数组相同之处:可以包含任意多个值,并能让你随心所欲地取用这些值;和数组不同之处:数组是以数字来编索引,散列则是用名称来找寻相应的值。
在散列里,没有固定的顺序,也没有所谓的第一个元素,它只是一些“键-值对”的组合。
散列的键全都是互不相同的任意字符串,但是它们对应的值却可能重复。
为什么使用散列?:由名字找姓;由主机名称找IP地址;由IP地址找主机名称;由单词找出单词在文件中出现的次数(常见的应用);由驾驶执照找出姓名等;
2)访问散列元素
语法: $hash{$some_key} 这和数组的做法类似,但是我们会在索引值(散列键)旁边使用花括号,而不是方括号。另外,键表达式现在是字符串,而不是数值:
$family_name{“fred”} = “filintstone”; $family_name{“barney”} = “rubble”;
当你对某个已存在的散列元素赋值,就会覆盖先前的值;
访问不在散列里的值会得到undef;
访问整个散列:
若要引用整个散列,请以百分比符号(%)开头,如%family_name。
为了方便起见,散列可以被转换成列表,反之亦然:
%some_hash = (“foo”,35,”bar”,12.4,2.5,”hello”,”Wilma”,1.72e30,”betty”,”byen”);
@any_array = %some_hash; #“展开”散列,“键-值对”列表
散列的赋值运算:
虽然很少这样做,但是散列可以用一般的赋值语法来复制:%new_hash = %old_hash;
比较常见的是以某种方式来转换散列,比如可以建立一个反序的散列:
%inverse_hash = reverse %any_hash;
注意,只有在确定原先的值互不重复的情况下,才应该使用此技巧!
大箭号:
依照Perl的文法,在任何需要逗号的地方都可以使用大箭号(=>)来代替:
my %last_name = ( #散列可以是词法变量
“fred” => “filintstone”,
“dino” => undef,
);
3)散列函数
keys和values函数:
keys函数会返回但前散列的所有的键,而values函数所返回的则是相应的每个值。假如散列里没有任何元素,则这两个函数都会返回空列表:
#!/usr/bin/perl -w
my %hash = ("a" => 1,"b" => 2,"c" => 3);
my @k = keys %hash;
my @v =values %hash;
print "@k are:@kn";
print "@v are:@vn";
my $count = keys %hash;
print "The counts of keys are:$count.n";
Perl不会维持散列中元素的顺序,但是,无论键的顺序如何,值的顺序都和它一样。
在标量上下文中,这两个函数都会返回散列元素的个数,它们会迅速执行这项任务,而无需逐项计算散列中的每一个元素: my $count = keys %hash; #变成3,表示3组“键-值对”
each函数:
假如你想对整个散列进行迭代,使用each函数是常见的做法,它会返回含有两个元素的列表,也就是“键-值对”。实际使用时,唯一合适用each函数的地方是在while循环中:
#!/usr/bin/perl -w
my %hash = ("a" => 1,"b" => 2,"c" => 3);
while ( ($key,$value) = each %hash ) {
print "$key => $valuen";
}
假如你需要依次处理散列,只要对键排序就可以了:
#!/usr/bin/perl -w
my %hash = ("a" => 1,"b" => 2,"c" => 3);
foreach $key (sort keys %hash) {
$value = $hash{$key};
print "$key => $valuen";
}
4)散列的典型用途
判断某项散列元素的真假:
if ($books{$someone}) {
Print “$someone has at least one book checked out.n”;
}
exists函数:
要查看某个键是否存在于散列中,使用exists函数,假如散列中有这个键,它就会返回真值,无论相应的值是否为“真”:
if (exists $books{“dino”}) {
print “Hey,there’s a library card for dino!n”;
}
delete函数:
delete函数将会从列表移除你所指定的键(和相应的值)。
my $person = "betty";
delete $books{$person}; #撤销$person的借书证
散列元素的替换:
可以将单一散列元素替换到双引号括住的字符串内,但我们并不支持整个散列的替换。
习题:
1)写一支程序,处理用户所指定的名字并且汇报相应的姓:
#!/usr/bin/perl -w
my %last_name = ("fred" => "filntstone","barney" => "rubble","wilma" => "flintstone");
print "Choices of fred,barney,or wilman";
chomp(my $name = );
print "That's $name $last_name{$name}.n";
参考答案如下:
#!/usr/bin/perl -w
my %last_name = qw (
fred filntstone
barney rubble
wilma flintstone);
print "Enter a first name:fred,barney or wilma.n";
chomp(my $name = );
print "That's $name $last_name{$name}.n";
2)写一支程序,读取一连串的单词,一行一个,直到输入结束,然后输出一份列出每个单词出现次数的列表:
#!/usr/bin/perl -w
print "Enter some words,each line for one word,then press Ctrl+Dn";
my(@words,%count,$word);
chomp(@words = );
foreach $word(@words) {
$count{$word} += 1;
}
foreach $word (keys %count) { #如果要排序,在keys前加上sort
print "$word was seen $count{$word} times.n";
}
正则表达式
1)何谓正则表达式
正则表达式在Perl里通常称为模式,是一个“匹配”或“不匹配”特定字符串的模板。
另一个用到正则表达式的地方就是Unix的grep命令,它会检查文本里哪几行匹配你所指定的模式,然后输出那几行的内容。
不要把正则表达式与shell的“文件名匹配模式”(又称为glob)弄混了。
2)使用简易模式
要以某个模式(正则表达式)来匹配$_的内容,请将它放在两个斜线(/)之间:
$_ = “yabba dabba doo”;
if (/abba/) {
print “It matched!n”;
}
由于模式匹配通常用来返回真或假值,所以往往会在if或while的条件表达式里看到它。
所有能在双引号内的字符串中使用的“反斜线转义字符”也都可以在模式里使用。
关于元字符:
点号(.)是通配符,它会匹配换行符(n)以外的所有单个字符,只能用来匹配一个字符。
反斜线也是元字符。
简易的量词:
星号(*)会匹配它的前一个项目0次或多次,如:/fredt*barney/
把星号想成“前面的东西可以重复出现任意多次,也可以是零次”可能会比较容易理解。
.*会匹配任意字符无限多次,“随便什么东西”模式,它匹配的是字符串中的随便什么东西
星号的正式名称是量词,意思是它指定了前一个项目的数量。
加号(+)也是量词,它会匹配前一个项目一次以上,如: /fred+barney/
把加号想成“不管前面出现什么,再接着随便几个相同的东西”或许会比较容易理解。
问号(?)也是量词,表示前一个项目是可有可无(出现一次或不出现)的。
因为这三个量词指定了前一个项目重复出现的次数,所以它们都必须接在某个东西之后。
模式组:
圆括号(或称小括号)用来归组,圆括号也是元字符。
模式/(fred)+/会匹配像fredfredfred这种字符串。
择一匹配:
竖线(|)在这里使用时通常念为“或”,表示左边匹配或邮编匹配。
模式/fred (and | or) barney/可用来匹配任何含有fred and barney或fred or barney的字符串。
3)字符集
字符集就是方括号(或称中括号)里一连串可能的字符。它只会匹配单一字符,但是该字符可以是字符集里的任何一个。可以使用连字符(-)来指定某个范围的字符,如[a-zA-Z]。
当然,字符集只是完整模式的一部分,它在Perl里永远不会单独出现。
#!/usr/bin/perl -w
$_ = "The HAL-9000 requires authorization to continue.";
if (/HAL-[0-9]+/) {
print "The string mentions some model of HAL computer.n";
}
在字符集开头加上插入记号(^)可用来排除字符集,如[^def]匹配这3个字符外的任何字符。
字符集简写:
代表任意数字的字符集[0-9]可被简写成d;简写w就是所谓的“单词”字符:[A-Za-z0-9_]
当然,w并不会匹配一个“单词”,它只会匹配“单词”里的一个字符。虽然如此,要匹配完整的单词时,只要使用加号修饰符就行了,如:/fred w+ barney/
s简写擅长处理空白,它相当于[ftnr],即换页、制表、换行、回车、空格共5中空白符。
比较常见的做法是使用s*来匹配任意数目的空白,或用s+来匹配一个以上的空白。
反义简写的排除:
以上3种简写的反义,只要使用它们的大写形式-D、W、S就可以了,它们所匹配的就是相应的小写形式所不匹配的字符。如/[dA-Fa-f]+/可以用来匹配十六进制数字。
另外一个复合字符集是[dD],表示任何数字或非数字,也就是会匹配任何字符!这是匹配任意字符(包括换行符)的常见做法。
习题:
1)使用测试程序并测试一个模式,让它匹配任何含有fred的字符串。
#!/usr/bin/perl -w
while (<>) {
if(/fred/) {
print "It matched!n";
}
}
参考答案如下:
#!/usr/bin/perl -w
while (<>) {
if(/fred/) {
print;
}
}
2)
#!/usr/bin/perl -w
while (<>) {
if(/[fF]red/) {
print;
}
}
3)
#!/usr/bin/perl -w
while(<>) {
if(/./) {
print;
}
}
4)
#!/usr/bin/perl -w
while(<>) {
if (/[A-Z][a-z]/) {
print;
}
}
5)
#!/usr/bin/perl -w
while(<>) {
if (/(wilma).*(fred)/) {
print;
}
}
参考答案如下:
#!/usr/bin/perl -w
while (<>) {
if (/wilma/) {
if (/fred/) {
print;
}
}
}
或者:#!/usr/bin/perl -w
while (<>) {
if (/wilma.*fred|fred.*wilma/) { #这样写是因为wilma和fred的顺序不确定
print;
}
}
以正则表达式进行匹配
1)以m//进行匹配
以/i来进行不区分大小写的模式匹配:
#!/usr/bin/perl -w
print "Would you like to play a game?";
chomp($_ = );
if (/yes/i) {
print "In that case,I recommend that you go bowling.n";
}
以/s来匹配任意字符:
#!/usr/bin/perl -w
$_ = "I saw Barneyndown at the bowling alleynwith frednlast night.n";
if (/Barney.*Fred/s) {
print "That string mentions Fred after Barney!n";
}
组合选项修饰符:
#!/usr/bin/perl -w
while (<>) {
if (/barney.*fred/si) {
print "That string mentions Fred after Barney!n";
}
}
绑定操作符=~
#!/usr/bin/perl -w
print "Do you like Perl?(yes/no)";
my $likes_perl = ( =~ /byesb/i);
if ($likes_perl) {
print "You said earlier that you like Perl,so ...n";
}
模式内的内插
#!/usr/bin/perl -w
my $what = "larry";
while (<>) {
if (/^($what)/) {
print "We saw $what in beginning of:$_";
}
}来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/21256317/viewspace-776992/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/21256317/viewspace-776992/