正在浏览 Perl 里的文章

列表(list):列表指的是标量的有序集合。
数组(array):是存储列表的变量

列表指的是数据,而数组指的是变量。列表的值不一定放在数组里,但每个数组变量都一定包含一个列表(即使该列表可能是空的)。如下图是一个列表,无论他是否存储在某个数组中:

包含5个元素的列表
数组或列表中的每个元素(element)都是单独的标量变量,拥有独立的标量值。这些值是有序的,也就是说,从起始元素到终止元素的先后次序是固定的。数组或列表中的每个值都有其索引值。

特殊的数组索引值

如何确定数组最后一个索引号是多少
$rocks[0]='a' # 一个元素
$rocks[1]='b' # 第二个
$rocks[2]='c'
$rocks[3]='d'
$rocks[99]='end' # 现在有95个undef 元素

那么 $#rocks 的值为99 因为$rocks索引的最后一个索引确实为99,数组中元素的个数为$#rocks+1 。
下面的方法是获得数组最后一个元素的值
print $rocks[-1]

列表直接量

在圆括号中用逗号隔开的一系列值,这些值构成了列表中的元素。如下:
(1,2,3) # 包含1、2、3这三个数字的列表
(1,2,3,) # 相同的三个数字,末尾的逗号忽略掉了
("fred",4.5) # 两个元素 fred和4.5
() # 空列表;零个元素
(1..100) # 100个整数构成的列表

上面最后一个例子用到了范围操作符(..)。该操作符会冲左边的数字技术到右边,每次加1,以产生一连串的数字。举例来说:
(1..5) # 含有1、2、3、4、5
(1.7..5.7) # 同上,小数部分舍去
(5..1) # 空列表,仅向上计数
(0,2..6,10,12) # 含有0、2、3、4、5、6、10、12
($m..$n) # 范围由$m和$n当前的值决定
(0..$#rocks) # rocks数组里的所有索引数字

正如最后两行所示,列表中的元素不必都是常量——他们也可以是表达式,每次用到这个表达式就会重新计算。

qw简写

在perl程序中经常需要建立简单的单词列表。这时使用qw简写,就不必键入许多无用的引号:
qw( fred barney betty willma dino ) # 更简洁

当然qw还可以使用任意的符号作为列表的定界符,如下
qw! fred barney betty willma dino !
qw/ fred barney betty willma dino /
qw# fred barney betty willma dino #
qw( fred barney betty willma dino )
qw{ fred barney betty willma dino }
qw[ fred barney betty willma dino ]
qw< fred barney betty willma dino >

最后四行所示,与前面的三个略有不同,如果起始定界符是偶中“左”字符,则结尾的定界符就是相应的“右”字符。如果是其他符号则前后定界符必须相同。

如果qw中需要有定界符的出现,可以使用反斜线转义一下,如下:
qw! fred barney \!betty willma dino !

下面的例子是保护一连串unix文件名的列表:
qw{
/usr/dict/words
/home/rootbeer/install
}

列表的赋值

($fred,$barney,$dino)=("a","b",undef); # 相当于做了3次独立的赋值操作
($fred,$barney)=($barney,$fred); # 互换两者的值
($fred,$barney)=qw< a b c d e> # 丢弃c、d、e
($fred,$barney)=qw< a > # $barney被赋值为undef

pop和push操作符

pop操作符可以去除数组中最后一个元素,同时返回该元素值:
@array=5..9;
#fred=pop(@array); $ $fred 变为9 @array 现在是(5,6,7,8)
$barney=pop @array; $ $barney 为8 @array 现在是(5,6,7)
pop @array; $ @array 为(5,6) 7被丢弃了

如果数组是空的,那么执行pop操作的话会返回undef

与pop对应的是push操作符
push (@array,0); # @array 为(5,6,0)
push @array,8; # @array 为(5,6,0,8)
push @array,1..10; # @array 得到了10个新元素
@others=qw/ 9 0 2 1 0 /;
push @array,@others; # @array 又得到了5个新元素(共19个)

注意:push的第一个参数或pop的那个唯一的参数必须是数组变量

shift和unshift操作符

pop和push都是针对数组的最后一个元素进行操作,那么shift和unshift则是针对数组的最前面的元素进行操作

@array=qw# dino fred barney #;
$m=shift(@array); # @m 变为 dino,@array 现在是fred,barney
$n=shift @array; # @n 变为 fred,@array 现在是barney
shift @array; # 现在@array 为空了
$o=shift @array; # $o为undef @array 还是为空
unshift(@array,5); # @array为5
unshift @array,4; # @array为4,5
@others=1..3;
unshift @array,@others; # @array为1,2,3,4,5

字符串中的数组内插

和标量一样,数组也可以放在双引号包含的字符串中。如果将数组直接书写到双引号包含的字符串中时,会在数组的各个元素之间添加空格:

#!/usr/bin/perl -w</span></span>

use strict;

my @fruit=qw/apple banana orange pear/;

print "my fruit :@fruit\n";
print "I like:$fruit[0]\n";

输出结果:
my fruit :apple banana orange pear
I like:apple

注意:
1、如果在我的数组后面还有其他字符时,需要将数组用括号括起来,如:print "my likes (@fruit) and some thing!";
2、如果我要在print语句中使用电子邮箱该如何书写呢?直接将@字符转义既可,如 print "zyqduron\@163.com";

foreach控制结构

foreach循环可以逐项遍历列表中的只,一次迭代(循环过程):
foreach $rock (qw/ bedrock slate lava /)
{
print "one rock is $rock.\n"        # 依次打印所有三种石头的名称
}

每次循环时,控制变量(control variable)(即此例中的$rock)都会从列表中取得新的值

控制变量并不是列表元素的复制品——实际上,他就是列表元素本身。也就是说,加入在循环中修改了控制变量的值,也就同时修改了这个列表元素。如下列子:

#!/usr/bin/perl -w
@rocks=qw/ bedrock slate lava /;
foreach $rock (@rocks)
{
$rock = "\t$rock";
$rock.= "\n";
}
print "The rocks are:\n",@rocks;

输出结果
The rocks are:
bedrock
slate
lava

注意:如果在foreach循环的靠头省略了控制变量的话,perl会使用默认的变量$_ 来取代。这个变量除了名称特别外语其他变量没有区别如下例子:

foreach (1..10)
{
print "I can count to $_!\n"
}

这种场合很多,如print,如果没有指定任何内容时他打印输出的为$_ 变量中的内容

reverse操作符

reverse可以反转列表或数组的值,如下:
@fred = 6..10;
@barney = reverse(@fred); # 得10,9,8,7,6
@wilma = reverse 6..10; # 同上,但不需要额外的数组
@fred = reverse @fred; # 恢复

reverse @fred; # 错误,不会修改 @fred
@fred = reverse @fred; # 这样才对

sort操作符

@rocks   = qw/ bedrock slate rubble granite /;
@sorted  = sort(@rocks);        # 会得到bedrock, granite, rubble, slate
@back    = reverse sort @rocks; # 逆序重 slate 到 bedrock
@rocks   = sort @rocks;         # 将排序后的结果存回 @rocks
@numbers = sort 97..102;        # 得到 100, 101, 102, 97, 98, 99

最后一个例子可以看出,将数字当成字符串来排序,这样的结果不太对。根据默认的排序规则,任何以1开头的字符串会被排在9开头的字符串之前。另外,它和reverse一样不会修改参数,所以要对数组排序时,你必须将排序后的结果存回数组:

sort @rocks; # 错误,这么做不会修改 @rocks
@rocks = sort @rocks; # 现在手机来的石头拍的井井有条

----------本章结束----------

Perl笔记:01、变量

抢沙发

字符型变量和数值型变量

数值型变量可以直接用 3*8 ,7+9的方式来书写

字符型变量的连接可以使用点(.)操作符,如 "abcd"."xxxx"等同于"abcdxxxx"

字符型变量可以内嵌到其他的字符型变量中,但要求的字符串必须用双引号来括起的形式,单引号的不能够内嵌
如 :
$ZYQ="ZhouYueQiu"
$NAME="my name is $ZYQ"

注意:如果$ZYQ没有赋值,那么将用空来代替

if控制结构

一旦能比较两个值时,就希望能根据这些比较结果作判断。和别的语言类似,Perl中也提供了 if控制结构:

if($name gt 'fred')
{
print "'$name' comes after 'fred' in sorted order.\n" ;
}

如果需要另一种选择,可以使用关键字else:

if($name gt 'fred' )
{
print "'$name' comes after 'fred' in sorted order.\n" ;
}else
{
print "'$name' does not come after 'fred'.\n";
print " Maybe it's the same string, in fact.\n" ;
}

Boolean 值

在if控制结构的条件判断部分可以使用任意的标量值。这在某些时候将很方便,如:

$is_bigger=$name gt 'fred';
if($is_bigger)
{
do something
}

那么,Perl 是怎么判断其值得true 或 false呢?Perl不同于其它的一些语言, 它没有Boolean 类型。它利用如下几条规则
●数字0 是false;其余为真
●如果值为字符串,则空串('')为false;其余为真
●如果值的类型既不是数字又不是字符串,则将其转换为数字或字符串后再利用上述规则

用户输入

现在,可能想你的 Perl 程序怎样才能从键盘上得到输入呢?有一种简单方法:使用行输入操作符(line-input operator),
<STDIN>
◆是行输入运算符对文件句柄STDIN的操作。但直到第五章才介绍文件句柄。
<STDIN>作为标量值来使用的,Perl 每次从标准输入中读入文本的下一行,将其传给<STDIN>。标准输入可以有很多种;
默认的是键盘。如果还没有值输入<STDIN>,Perl 会停下来等你输入一些字符,由换行符结束(return)。
◆坦白讲,是你的系统等待输入,Perl等待你的系统。具体的细节与机器和配置有关。由于是系统而非 Perl 控制你的输入,因此要更正错误的输入通常可以在按下回车前使用退格键(backspace) 。如果想更多的控制输入,可以使用 Term::ReadLine 这个模块

<STDIN>中的字符串通常由一个换行符作为结尾。因此,可以如下操作:
◆例外的情况是,标准输入流在行中间就结束了。当然,普通的文本文件通常不是这样。
例1-1

#!/usr/bin/perl

print "please input you name:";
$NAME=<STDIN>;

if ( $NAME eq "" )
{
print "please input your name!";
}
else
{
print "you name is :$NAME";
}

chomp函数

chomp函数,可以去掉变量中最后的换行符

如下例:
$text="a line of text\n" ; #也可以由<STDIN>输入
chomp($text); #去掉换行符(\n)。

这是将字符串末尾换行符去掉的最好方法。
基于 Perl 中的一条基本原则:在需要使用变量的地方,可以使用赋值表达式来代替。
我们有更简单的使用 chomp的方法。Perl 首先做赋值运算,再使用这个变量。因此
使用chomp的最常用方法是:
chomp ($text = <STDIN>); #读入,但不含换行符

$text = <STDIN>;
chomp ($text); #同上,但用两步完成

chomp是一个函数。作为一个函数,它有一个返回值,为移除的字符的个数。这个数字基本上没什么用:
$food=<STDIN>;
$betty=chomp $food; #得到值1
如上,在使用 chomp 时,可以使用或不使用括号() 。这又是 Perl 中的一条通用规则:除非移除它们时含义会变,否则括号是可以省略的。

while控制结构

和许多的程序语言一样,Perl 也提供了循环结构。while语句可以循环执行其内部的一块代码直到其条件非真:

$count = 0;
while ($count < 10) {
$count + = 2;
print "count is now $count\n" ; #打印出 2 4 6 8 10
}

条件中真假值的判断和if结构中是一样的。和if控制结构相同,花括号是必须的。判断条件在迭代前执行,如果条件为假,
则一次也不执行。

undef值

如果没有定义就是用这个变量的话,这个变量中的值就称之为undef。如果当成数字的话其值为0,当成字符串使用的话它表示空字符串。但它及不是数字也不是字符串,它完全是另一种类型的标量值。
既然undef作为数字时会被视为0,我们可以很容易的构造一个数值累加器,如下例子:
例1-2

#!/usr/bin/perl -w
while($n<10)
{
$n+=1;
print "$n\t";
}
print "\n"

输出结果
Use of uninitialized value in numeric lt (<) at undef.pl line 2.
1       2       3       4       5       6       7       8       9       10

这里我使用了-w参数因此输出了一行警告信息

同样undef也可以作为一个字符串的累加器,如下:
$string.="more text\n";

defined函数

"行输入"操作符 <STDIN> 在读到文件末尾时会返回undef。那么如何判断一个变量是undef还是空字符串呢?使用defined函数就可以搞定这个疑虑,如果是undef返回假,否则返回真。如下例子:
例1-3

#!/usr/bin/perl -w

$madonna = <STDIN>;

if (defined($madonna))
{
print "the input is $madonna\n";
}
else
{
print "No input available!\n";
}

如果想尝试undef值,可以直接使用undef操作符,或者不进行任何的定义即可。

---------本章到此结束----------

上一章已经简单的介绍了正则表达式的应用,在本章会介绍在Perl中如何应用这些正则表达式。

以m//进行匹配

我们已经在上章简单的使用双斜线的方式来编写模式(注意这里的模式实际上指的就是正则表达式),像/fred/。但实际上,这是m//(模式匹配)操作符的简写。就像我们在说明qw//操作符时提到的,可以选择使用任何成对的定界符。所以,我们可以把同样的表达式写成m(fred)、m<fred>、m{fred}或m[fred]。

这里有个简写:如果你选择双斜线作为定界符,那么你可以省略开头的m。所以大部分的模式匹配都会以双斜线来编写。

用/i来进行行大小写无关的匹配

使用/i修饰符,可以在进行模式匹配时不区分大小写,使得你能够轻易匹配FRED、fred或Fred:

print "Would you like to play a game? ";
chomp($_=<STDIN>);
if(/yes/i) { # 大小写无关的匹配
print "In that case,I recommend that you go bowling.\n";
}

用/s来匹配任意字符

默认情况下,点号(.)无法匹配换行符,这对大多数单行匹配的情况是合适的。但如果字符串中含有换行符,而你希望点号能用来匹配他们,那么/s修饰符可以完成这个任务。它会将模式中的每个点号按字符集[\d\D]的效果来处理,就是会匹配任意字符(包括换行符)。下面的实例就是一个包含换行符的字符串,来看看:

$_ = "I saw Barney\ndown at the bowling alley\nwith fred\n last night.\n";
if (/Barney.*fred/s) {
print "That string mentions fred after Barney!\n";
}

因为这两个名称并不在同一行,所以如果省略/s修饰符,上述的匹配就会失败。

用/x加入空白

说白了这个修饰符就是为了在模式中加入注释使用的,如果一个很复杂的模式不加入注释的话可能时间长了自己写的模式都会不认识了。如下例子:

/
-?                    # 零个或一个减号
\d+                  # 一个或多个数字
\.?                   # 零个或一个小数点
\d*                  # 零个或多个数字
/x                    # 字符串结尾

组合选项修饰符

如果在一个模式中使用多个修饰符,可将它们连在一起使用,它们之间的先后顺序并不会影响匹配的结果

$_ = "I saw Barney\ndown at the bowling alley\nwith fred\n last  night.\n";
if (/Barney.*fred/is) {
print "That string mentions fred after Barney!\n";
}

将同样的模式展开并加上注释后的模样:

$_ = "I saw Barney\ndown at the bowling alley\nwith fred\n last  night.\n";
if (m{
barney # 小伙子 barney
.*            # 之间的任何东西
fred       # 大嗓门的 fred
}six) {  # 同时使用/s 、/i和/x
print "That string mentions fred after Barney!\n";
}

锚位

默认情况下,模式匹配的过程开始于待匹配字符串的开头,如果不相符就一直往字符串后面浮动,看其他位置能否匹配。但是加入一些锚位,可以让模式直接匹配字符串的某处。
脱字符(^)用来标示字符串的开头
美元符号($)用来标示字符串的结尾
/^fred/只匹配位于字符串最前端的fred,如果是manfred mann这个字符串,则不能匹配。而/rock$/也只匹配位于字符串最后面的rock,如果是knute rockne,也同样失败。

某些时候,这两个锚位会一起使用,以确保模式可以匹配整个字符串,这个常见的实例是/^\s*$/,这个模式可以匹配空白行。

单词锚位

锚位并不局限于字符串的首尾。比如\b是单词边界锚位,它匹配任何单词的首尾。因此/\bFred\b/可以匹配fred,但无法匹配frederick、alfred或manfred mann。这在文字处理器的搜索命令里,通常称为证词搜索模式。

不过,这里所说的单词并不是一般的英文单词,而是由一组\w字符构成的字符集,也就是有普通英文字母、数字与下划线组成的单词。\b锚位匹配的是一组\w字符的开头或结尾。

在下图中,每个此下发会出现灰色下划线,\b会匹配的位置则以箭头标识。因为每个单词都会有开头与结尾,所以字符串中的单词边界一定是偶数个。

用\b匹配单词边界

此处所谓的单词是指一连串的字母、数字与下划线的组合,也即是匹配/\w+/模式的字符串。该句子共有5个单词:That、s、a、word以及boundary。word两边的引号并不会改变单词边界。这些单词是由一组\w字符组成的。

非单词边界锚位是\B,他能匹配所有\b不能匹配的位置。因此模式/\bsearch\B/会匹配searches、searching与searched,但不会匹配search或researching。

绑定操作符

默认情况下模式匹配的对象是$_,绑定操作符=~则能让Perl拿右边的模式来匹配左边的字符串,而非匹配$_。例如:

my $some_other = "I dream of betty rubble.";
if ($some_other =~ /\brub/) {
print "Aye,there's the rub.\n";
}

绑定操作符虽然看起来有些像赋值操作符,但它表示不要将模式匹配$_而是将模式匹配我左侧的字符串吧!

下面的这个例子,$likes_perl会被赋予一个布尔值,这个结果取决于用户输入的内容。这个程序属于“急功近利”型的,因为判断之后就丢弃了用户的输入。下面的代码的功能是读取输入行,匹配字符串与模式,然后舍弃输入行的内容。没有进一步使用$_,也没有改变它。

print "Do you like Perl? ";
my $likes_perl = () =~ /\byes\b/i);
if ($likes_perl) {
print "You said earlier that you like Perl.so...\n";
# ... 其他操作
}

因为绑定操作符的优先级相当高,也就没有必要用圆括号来括住模式测试表达式。所以下面这一行如同上面的表达式一样,会将匹配结果(而非该行输入的内容)存进变量:
my $likes_perl =  =~ /\byes\b/i;

模式串中的内插

正则表达式里可以进行双引号形式的内插。这让我们可以很快写出如下类似grep的程序:

#!/usr/bin/perl -w

use strict;

my $what = "zyq";
while(<>) {
if (/^($what)/) {
print "Yes you are $what\n";
}
}

无论$what的内容时什么,当我们进行模式匹配的时候,该模式都会成为$what的值。这里和/^zyq/是相同的意思,也即是在每行的开头寻找zyq。但是,$what不一定来自字符串直接量,我们可以从@ARGV里的命令行参数来取得:

my $what = shift @ARGV;

如果命令行的参数是zyq|wcl,则模式会变成/^(zyq|wcl)/,也就是在每一样的开头寻找zyq或wcl。这个模式中的括号很重要,如果没有它们,模式就会在字符串的开头匹配zyq或者在字符串的任何地方匹配wcl。

注意:请记住,除非while循环的条件表达式中只有整行输入操作符(<STDIN>),否则输入行不会自动存入$_

捕获变量

到目前为止,我们在模式中使用括号的时候只是为了区分不同的模式组。但括号在模式中还有捕捉变量的功能。所谓的捕捉变量实际上指得是,把(圆括号中模式所匹配的)部分字符串暂时记下了的能力,如果有一对以上的圆括号,就会有一次以上的捕捉。每个被捕捉的对象是原本的字符串,而不是模式。

因为捕捉变量存储的都是字符串,所以它们都是标量变量。在Perl里它们的名字类似$1或者$2。模式里的括号有多少对,匹配变量就有多少个。

这些变量可以去除字符串里的某些部分,因此是正则表达式威力强大的重要原因之一:

$_ = "Hello there, neighbor";
if (/\s(\w+),/) {             # 捕获空白符合逗号之间的单词
print "the word was $1\n";  # the word was there
}

当然也可以一次捕获多个字符串:

$_ = "Hello there, neighbor";
if (/(\S+) (\S+), (\S+)/) {
print "words were $1 $2 $3\n";
}

上面的程序输出的单词时Hello there neighbor,但输出时没有逗号的,因为模式里的逗号放在圆括号的外面,所以第二次捕捉不会有逗号。使用这个技巧,我们可以精确筛选捕获的(和跳过的)数据。

捕获变量的生命周期

捕获变量只应该在匹配成功时使用;否则就会得到之前一次模式匹配的捕获内容。下面的模式匹配失败的例子本来应该输出从$_捕获的某个单词。但是,如果比对失败,他会输出可能遗留在$1里的任何字符串:
$wilma =~ /(\w+)/;  # 这里的结果不一定正确
print "Wilma's word was $1... or was it?\n";

这就可以看出,为什么模式一般都要写在if或while的条件表达式中

#!/usr/bin/perl -w

use strict;

my $wilma = "hello the world!";
if ($wilma =~ /(\w+)/) {
print "Wilma's word was $1.\n";
} else {
print "Wilma doesn't have a word.\n";
}

捕获的内容不能够永久保存,因此像$1之类的匹配变量如果我们在后面的程序中想要使用其里面的内容,那么最好的方法就是将它赋值给其他定义好的变量,这么做也使得程序代码更容易阅读:

if ($wilma =~ /(\w+)/) {
my $wilma_word = $1;
...
}

不捕获模式

既然括号在模式中有分组和捕获的功能,那如果我在一个模式中即想用分组,又想用捕获,但我却不想捕获第一个本意为分组的内容,那又怎么办呢?当然我可以使用$2、$5之类的变量来代替,但若我就想用$1、$2又当如何处理呢?看看下面的例子:

if (/(?:bronto)?saurus (steak|burger)/) {
print "Fred wants a $1\n";
}

正如上面的例子所示,使用(?:)的形式就可以不捕获这个模式了!

命名捕捉

上面已经讲了可以使用括号的捕捉能力并且在$1、$2这样的变量中存取捕捉的串。但是即使对于较为简单的模式来说,管理这样的数字变量也是比较困难的,更别说大的模式匹配了。所以要想一个便于识别的变量来代替。在Perl 5.10引入了正则表达式命名捕捉的概念。它会将捕捉的记过存入一个特殊的哈希%+,其中的键就是在捕捉时候使用的特殊标签,其中的值则是被捕捉的串。
为捕获串加标签的方法:(?<LABEL>PATTERN)
这里的LABEL可以自行命名,下面的例子中我将第一个捕捉标签定为name1,第二个为name2。当然在使用捕捉串的时候需要访问的位置也变成了$+{name1}$+{name2}

use 5.010;
my $names = 'Fred or Barney';
if( $names =~ m/((?<name2>\w+) (and|or) (?<name1>\w+))/ ) {
say "I saw $+{name1} and $+{name2}";
}

自动匹配变量

除了上面介绍的捕获变量外还有3个另类的匹配变量。他们是:$&、$`、$' 含义如下:

$& 字符串里实际匹配模式的部分自动存入该变量
$` 匹配起始位置之前的内容存放在改变量
$'  匹配结束位置之后的内容存放在该变量

看看下面的实例:

if ("Hello there, neighbor" =~ /\s(\w+),/) {
print "That was ($`)($&)($').\n";
}

输出结果为:That was (Hello)( there,)(neighbor).

使用自动匹配的弊端
如果在程序中使用了自动匹配变量,则其他正则表达式的速度会变慢,虽然不会严重的拖慢速度,但很多程序员还是不怎么喜欢使用这些变量。通常使用带编号的匹配变量也就足够了。本书的后面章节实例中还会看到这些变量的使用。

通用量词

模式中的量词代表前置条目的重复次数。目前已经看到的有:*、+和?。在这里我们再介绍一个({})花括号,花括号里指定重复次数的范围。因此:
/a{5,15}/ 可以匹配重复出现5到15次的字母a
/(fred){3,}/ 这个会匹配重复出现3次以上的fred
/\w{8}/ 会匹配正好8个字符的单词串。注意是8个字符并不是说8个重复的字符

事实上,我们之前提到的三个量词字符都只是常用的简写而已。星号相当于{0,},加号相当于{1,},问号相当于{0,1}。

=====================本章习题=========================

1、利用模式测试程序,写个模式,使其能够匹配到match这个字符串,你可以把beforematchafter输入到程序里测试,看是否会正确显示匹配到的部分以及前后的部分?
答:exercise_8_1.pl

#!/usr/bin/perl -w

use strict;

$_="beforematchafter";

if (/(match)/i) {
print "$`\n$1\n$'\n";
}

输出结果为:
before
match
after

2、利用模式测试程序,写个模式,使其能够匹配任何以字母a结尾的单词(以\w组成的单词)。此模式是否能够匹配到wilma?是否无法匹配到barney?此模式是否能够匹配到Mrs._Willma_Flintsone?还有wilma&fred呢?把这些测试字符串加到该文本文件里测试程序。
答:exercise_8_2.pl

#!/usr/bin/perl -w

use strict;

while(<>) {
if (/\b(\w*a)\b/) {
print "$1 in $_\n";
}
}

letter.txt

wilma
barney
Mrs. Wilma Flintstone
wilma&fred

输出结果为:
wilma in wilma
Wilma in Mrs. Wilma Flintstone
wilma in wilma&fred

3、修改上一题的程序,使其定在定位以a结尾的单词后,在将之后的5个字符(如果有那么多的话)捕获至一个独立的内存变量。修改程序输出,把所用到的这两个内存变量都输出来。
exercise_8_3.pl

#!/usr/bin/perl -w

use strict;

while(<>) {
if(/(\b\w*a\b)(.{0,5})/s){
print "$1 after is $2 in $_\n";
}
}

输出结果为:
wilma after is    # 这里实际上是一个换行符
in wilma
Wilma after is  Flin in Mrs. Wilma Flintstone # Flin的前面还有一个空格
wilma after is &fred in wilma&fred
Wilma after is  yest in I saw Wilma yesterday # yest前面也有一个空格

4、写个新程序,输出其输入中以空白结尾的行。
答:exercise_8_3.pl

#!/usr/bin/perl -w

use strict;

while(<STDIN>){
if(/( $)/){
print "you input string end is space!\n";
} else {
print "no space!\n";
}
}

输出结果
abcde
no space!
okok_ # 这里最好的下划线实际上是一个空格,这里为了方便查看
you input string end is space!

--------------本章到此完毕-------------

什么是正则表达式

正则表达式在perl又称作模式,是一个匹配(或不匹配)某字符串的模板

使用简易模式

如果模式匹配的对象是$_的内容,只要把模式写在一对反斜线(/)中就可以了,如下所示:

$_="yabba dabba doo";
if(/abba/) {
print "It matched!\n";
}

表达式/abba/会在$_中寻找这4个字符组成的串,如果找到就返回真,这里会找到不止一个字符串,但这并不是关键,只要曾经找到过,匹配结果就为真,否则为假。

所有在双引号圈引的字符串中使用的技巧(尤其是正斜线转义)都可以在模式串里使用。因此/cake\tsprite/这个模式会匹配cake一个制表符和sprite这11个字符。

关于元字符

所谓的元字符就是在正则表达式中表达的特殊含义

点号(.)是任何单字符的通配符,当然换行(也就是\n)除外。点号只能用来匹配一个字符。如果要匹配点号怎么办呢?使用反斜线(\)来转移点号即可。反斜线是我们遇到的第二个元字符,在以后的文章中会陆续介绍更多的元字符。

简单的量词

*(星号):匹配前面的内容0次或多次。.*会匹配任意字符无数多次,我们经常戏称.*为捡破烂模式,因为他能通吃所有的字符串。

+(加号):匹配前面的内容1次或多次。把加号想成“算上刚才说的,再加上任意次重复”

?(问号):匹配前面的内容0次或1次。这可以理解为“刚才所说的,有没有都行”

因为这三个量词指定了前一个条目重复出现的次数,所以它们都必须接在某个东西之后。

模式分组

在数学中,圆括号((),或称为小括号)用来分组。因此,圆括号也是元字符。如:模式/fred+/会匹配像fredddddddd这样的字符串,可是这种字符串实际上不常出现。不过,模式/(fred)+/会匹配像fredfredfred这样的字符串,这可能才是你想要的。那么,模式/(fred)*/又如何呢?他会匹配像Hello,world这样的字符串。

圆括号同时也使得部分字符串重新引用称为可能。我们可以用反向引用来引用圆括号中(的模式所)匹配的文字。\1、\2这样的写法就是在使用反向引用。而斜线后面的数字和括号的组号匹配。

使用圆括号包围的点号可以匹配任何非回车字符。我们可以用如下程序反向引用刚刚匹配的字符\1:

$_="abba";
if (/(.)\1/) { # 同 'bb' 相匹配
print "It matched same character next to itself!\n";
}

(.)\1表明需要匹配连续出现的两个同样的字符。(.)会首先匹配a,但是在匹配反向引用的时候就会发现下一个字符不是a,导致匹配失败。perl会跳过这个字符,用(.)来匹配下一个字符b,在匹配反向引用的时候会发现下一个字符也是b,这样就构成了成功的匹配。
反向引用不必总是附在相应的括号后面。下面的模式会匹配y后面的四个连续的非回车字符,并且用\1在d字符之后重复这4个字符:

$_="yabba dabba doo"
if (/y(....) d\1/) {
print "It matched the same after y and d!\n";
}

也可以用多个括号来分成多组,每组都可以有自己的反向引用。我们可以用括号定义一个非换行字符的组,然后再跟一个非欢呼字符的组。然后用反向引用\2和\1来构成又去的回文模式,比如abba:

$_="yabba dabba doo"
if (/y(.)(.)\2\1/) { # 同 'abba' 相匹配
print "It matched the same after y and d!\n";
}

Perl 5.10 有一种新的反向引用写法。不再只是简单地用反斜线和组号,而是用了\g{N}这种写法。其中N是想要反向引用的组号。在刚才的例子中使用这种新写法会更加清晰:

$_="aa11bb"
if (/(.)\g{1}11/) {
print "It matched!\n";
}

用\g{N}写法的额外好处是,我们可以使用负数。相比绝对位置的反向引用,相对反向引用会更加有趣。这样我们可以用-1来表达刚才的模式:

择一匹配

竖线(|)通常可以读成“或”,意思是左边匹配或者右边匹配都行。也就是说如果左边的模式匹配失败了,还可以用右边的再试试。

字符集

字符集是指一串可能出现的字符集合,通过写在方括号([])内来表示。它只能匹配单个字符,但可以是字符集里列出的任何一个。
●[a-zA-Z] 匹配一个字母,大小写均可
● [0-9] 匹配一个数字
●[^abc] 匹配除了a或b或c之外的所有字符

字符集简写

[0-9] 等同于 \d

[A-Za-z0-9_] 等同于 \w
当然,\w并不会匹配一个单词,它只会匹配单词字符集里的一个字符。虽然如此,要匹配完整的单词时,只要使用加号修饰符就行了。像/fred \w+ barney/这样的模式,会匹配fred、一个空格、一个单词再接一个空格与barney。也就是说,如果在fred与barney之间有一个两边用空格隔开的单词,就会被这个模式匹配。

[\f\t\n\r ] 或者说是 空白 等同于 \s
也就是说,它等于是包含了5种空白的字符集:换页、制表、换行、回车以及空格。和之前其他的简写一样,\s只会匹配字符集里的某个字符。因此。比较常见的做法是使用\s*来匹配任意数目的空白(也包括了零个空白),或用\s+来匹配一个以上的空白字符。

反义简写

有些时候只是为了获得以上几种简写的反义,而必须写出类似[^\d]、[^\w]或是[^\s]这样的模式,来表示一个非数字、非词或者非空白字符。其实他们的大写版本就是用来完成这种任务的:也就是\D\W或者\S。这些大写版本能匹配相应小写版本范围以外的字符。

这些简写既可以作为模式里独立的字符集,也可以作为方括号里字符集的一部分。也即是说,/[\dA-Fa-f]+/可以用来匹配十六进制数字,十六进制表示法使用了ABCDEF(或abcdef)作为额外的数字。