文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>Perl语言入门笔记:第十二天

Perl语言入门笔记:第十二天

时间:2010-09-03  来源:linscora

第八章:
以正则表达式进行匹配:
正则表达式如何与Perl接轨呢?
以m//进行匹配
前面我们用/fred/要编写模式,事实上,它是m//(模式匹配)操作符的简写。也可以用m() m<> m{} m[]这样的模式。
用/^http:\/\//匹配http:// 和m%^http://%是一样的 可选修饰符:
一些可有可无的修饰符有时候称为开关。它们可以成组附加在某个正则表达式结尾的定界符的右边,并改变正则表达式的默认行为。
用/i来进行大小写无关的匹配:  

#!/usr/bin/perl -w

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来匹配任意字符(特别是换行)

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


用/x加入空白:
/x可以在模式里面随意加上空白,使//里面的内空更容易阅读和理解。
/-?\d+\.?\d*/  #挤在一起,很难看清是什么意思。
/ -? \d+ \.? \d* /x #加入空白后,稍微清楚些。
加入/x之后模式里面可以任意插入空白,所以原始的空白与制表符就推失去意义了,它们会被忽悠。如果还要匹配空白与制表符的话,就得在前面补上一个反斜线字符(也可以采用其他方式),不过\s(\s*)(\s+)还是比较常见的匹配空白的写法。

Perl里,注释也算一种空白。所以我们可以借机在模式里写上注释以指明其用途。
/
  -?    #零个或一个减号
  \d+  #一个或多个数字
  \.? #零个或一个小数点
  \d* #零个或多个数字
  /x #字符串结尾
 
\#  表示真正的#号

组合选项修饰符:
如果在一个模式中使用多个修饰符,可将它们连在一起使用。它们之间的先后

if (/barney.*fred/is) { #同时使用/i 和 /s

  print "That string mentions Fred after Barney!\n";
}

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

if (m{
    barney        #小伙子barney

    .*            #之间的任何东西

    fred        #大嗓门的fred

    }six) {        #同时使用/s、/i和/x

     print "That string mentions Fred after Barney!\n";
    }

注意,些处花括号为定界符,这让程序员专用的编辑器可以方便地(在正则表达式的两头间)跳转。

锚位:
默认情况下,模式匹配的过程开始于待匹配字符串的开头,如果不相符就一直往字符串后面浮动,看其他位置能否匹配。但是加入一些锚位,可以让模式直接匹配字符串的某处。

脱字符(^)是一个锚位,用来标示字符串的开头,而美元符号($)也是一个锚位,用来标示字符串的结尾。
/^fred/只匹配位于字符串最前端的fred,如果manfred mann这个字符串,则不能匹配。而/rocks$/也只匹配位于字符串最后面的rock,如果是knute rockne,也同样失败。值得注意的是:/^fred$/会匹配fred与fred\n这两个字符串。

两个锚位会一起使用,以确保模式可以匹配整个字符串。/^\s*$/这个模式可以匹配空白行。

单词锚位:
锚位并不限开字符串的首尾。比如\b是单词边界锚位,它匹配任何单词的首尾。
如:/\bFred\b/可匹配fred但无法匹配fredrick、alfred或manfredmann。这在文字处理器的搜索命令里,通常称为整词搜索模式。
不过,这里所说的单词并不是一般的英文单词,而是由一组\w字符构成的字符集,也就是由普通英文字母、数字与下划线组成的单词。\b锚位匹配是一组\w字符的开头或结尾。

用\b匹配单词边界:
/\bhunt/匹配 hunt hunting hunter  排除了shunt
/stone\b/匹配 standstone  flintstone 

用\B匹配非单词边界锚位:
/\bsearch\B/ 匹配searchs searching searched  但不匹配search或researching

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

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

#绑定操作符虽然看起来像某种赋值操作符,其实并非如此!它只表示本来这个模式会匹配$_变量,但请针对左边的字符串匹配吧。若没有绑定操作符,表达式就会使用默认的$_。


#!/usr/bin/perl -w

print "Do you like Perl?";
my $likes_perl = (<STDIN> =~ /\byes\b/i);  ## my $likes_perl = <STDIN> =~ /\byes\b/i;

...        #other actions;

if ($likes_perl) {
  print "you said earlier that you like Perl, so...\n";
  ...
}

在上面例子里,$likes_perl会被赋予一个布尔值,这个结果取决于用户键入的内容。判断之后丢弃了用户的输入。
这行代码大致上的功能是读取输入行,匹配字符与模式,然后舍弃输入行的内容。没有进一步使用$_,也没有改变它。

模式串中的内插:

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

#!/usr/bin/perl -w

my $what = "larry";

while (<>)
  if (/^($what)/ {    #模式的锚位被定在字符串的开头

  print "We saw $what in beginning of $_";
  }
}

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

$what不一定来自字符串直接量,我们可以从@ARGV里的命令行参数来取得:
my $what = shift @ARGV;
如果第一个命令行参数是fred|barney,刚模式会变成/^(fred|barney)/,也就是在每一行开头寻找fred或barney。寻找larry时多余的圆括号现在变得很重要,如果没它们,模式就会在字符串开头匹配fred或者在字符串任何地方匹配 barney。

如果$what的值为fred(barney),则模式就会变成/^(fred(barney)/,你也不知道这样是不会正确工作的,它会以invalid regular expression错误平中断你的程序。有一些高级技巧,可捕获这类错误(或者从一开始就解除元字符的魔法),让它不再中断你的程序。不过目前只需要记住,一旦赋予使用正则表达式的权力,他们就该负起正确使用的责任。

捕获变量:

每当我们在模式里使用圆括号的时候,都只是用来表示不同的模式组。但圆括号同时也启动了正则表达式处理引擎的捕获功能。捕获功能指的是,把(圆括号中模式匹配的)部分字符串暂时记下来的能力。如果有一对以上的圆括号,就会有一次以上的捕获。每个被捕获的对象是原本的字符串,而不是模式。

$_ = "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
注意这里there没有逗号。因为模式里的逗号放在圆捂外面,所以第二次捕获中不会有逗号。
使用这个技巧,我们可以精确筛选捕获的(和跳过的)数据。

有时可能产生空匹配变量,因为那部分的模式允许为空,也就是说要考虑捕获变量为空的情况。

#!/usr/bin/perl -w

my $dino = "I fear that't I'll be extinct after 1000 years.";
if ($dino =~ /(\d*) years/) {
  print "That said '$1' years.\n";  #$1 为 1000

}

$dino = "I fear that I'll be extinct after a few million years.";
if ($dino =~ /(\d*) years/) {
  print "That said '$1' years.\n";    #$1为空字符串

}

捕获变量的生命期:
这些捕获变量通常能存活到下次成功的模式匹配为止。也就是说,失败的匹配不会改动上次成功匹配时捕获的内容,而成功的匹配会将它们重置。换句话说。捕获变量只应该在匹配成功时使用;否则就会得到之前一次模式匹配的捕获内容。

#!/usr/bin/perl -w

$wilma =~ /(\w+)/;        #不对!这里的结果不一定正确

print "Wilma's word was $1... or was it?\n";

#这就是为什么模式匹配总是出现在if或while条件表达式里:

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

不捕获模式:
目前所见的圆括号都会捕捉部分的匹配串到捕获变量中,但有时候却需要关闭这个功能,而仅仅只是用它来进行分组。

#!/usr/bin/perl -w

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

Perl只是按左圆括号的序号来决定捕获变量名,这导致我们真正想捕获的内容只能进入$2。
还好Perl的正则表达式允许使用括号但不作捕捉。我们把这叫做不捕捉括号,书写时候有些差别。需要在左括号的后面加上问号和冒号(?:),以告知Perl这一对括号完全是为了分组而存在的。

?在正则表达式中有四种用法:原文问号(需要转义) 有无量词  非贪婪修饰符  不捕捉前缀 这四种。

下面我们用?:来跳过bronto,这样就可以用$1来捕获我们要的内容了。

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


命名捕捉:
虽然可以用括号的捕捉能力并且在$1、$2这样的变量中存取捕获的串。但是即使对于较为简单的模式来说,管理这样的数字变量比是比较困难的。

#!/usr/bin/perl -w

use 5.010;
my $names = 'Fred or Barney';
if($names =~ m/(\w+) and (\w+)/) {    #不会被匹配

   say "I saw $1 and $2";
   }

这里因为不匹配,所以我们看不到say的输出。这是因为模式串中的期望是and,而实际变量$names中提供的是or。我们考虑之后认为应该允许两者并存。所以正则中加入“择一”平匹配and或者or。

use 5.010;
my $names = 'Fred or Barney';
if ( $names =~ m/(\w+) (and|or) (\w+)/ ) {    #现在可以匹配了

    say "I saw $1 and $2";
}

这个say输的结果将是 I saw Fred and or,这也不是我们要的结果。

在Perl5.10引入了正则表达式命名捕捉的概念。现在捕捉的结果会进入一个特殊的哈希%+,其中的键就是在捕捉时候使用的特殊标签,其中的值则是被捕获的串。为捕获串加标签的方法是使用(?<LABEL>PATTERN)这样的写法,而LABEL可以自行命名。

#!/usr/bin/perl -w

use 5.010;
my $names = 'Fred or Barney';
if($names =~ m/(?<name1>\w+) (?:and|or) (?<name2>\w+)/) {    
   say "I saw $+{name1} and $+{name2}";
   }
   
#现在输出的结果将是:I saw Fred and Barney


#一旦使用了捕捉标签,就可以随意移动位置并加入更多的捕获括号,不会因为括号的次序变化导致麻烦。

#!/usr/bin/perl -w

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

在使用捕捉标签之后,也给反向引用带来了更新的必要。之前我们使用\1或者\g{1}这样的写法,现在我们可以使用\g{label}这样的写法。   \k<label> 等效于\g{label}  可是\g{N}可以实现反向引用。

#!/usr/bin/perl -w

my $names = 'Fred Flinstone and Wilma Flinstone';
if( $names =~ m/(?<last_name>\w+) and \w+ \g{last_name}/ ) {
    say "I saw $+{last_name}";
}

Perl也允许用Python的语法(?P<LABEL>...)完成同样的功能。

自动匹配变量:

有三个不请自来的捕获变量,不必使用捕捉圆括号就能引入。这看来真是不错,但是这些变量的名称很奇怪。它们是: $& $` $'

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

匹配起始位置之前字符串会存到$`里,而匹配结束位置之后的字符串则存到$'里。另外一个解释方法是。$`保存了正则表达式引擎在找到匹配段落之前略过的部分,而$'则保存了字符串中剩下的、从来没有匹配到的部分。如果将这三个字符串依次连接起来,就一定会得到原来的字符串:

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

#用来显示各个部分匹配的串:


print "Matched:|$`<$&>$'|\n";    

通用量词:
模式中的量词代表前置条目的重复次数。到目前为止,我已经经见过三个量词:* + 和?。如果这三个量词都不符合需要。如果这三个量词都不符合需要,还可以在花括号({})里指定重复次数的范围。

/a{a,15}/  可匹配重复出现5到15次的字母a。
/(fred){3,}/ 这个模式第二个数字没有但保留逗号,表次没有上限。
/\w{8}/   表示8个字符的单词。
* 和 {0,} 一样
?  和  {0,1} 一样

优先级:
正则表达式优先级
正则表达式特性    例子
圆括号(分组或者捕获)  (...), (?:...), (?<LABEL>...)
量词      a* a+ a? a{n,m}
锚位和序列     abc  ^a  a$
择一      a|b|c
元素      a [abc] \d \1

优先级实例:
/^fred|barney$/   如果是这样的话,表示要不匹配字符串开头的fred,要不匹配字符结尾的barney
/^(fred|barnet)$/  匹配只包含fred或是只包含barney的每一行。 
/(wilma|pebbles?)/ 这个问号量词会紧接着前面的字符。所以它可以匹配到willma pebbles pebble
/^(\w+)\s+(\w+)$/  匹配开始是一个单词、再来是一些空白、然后又是一个单词的行

模式测试程序:
#!/usr/bin/perl -w

while (<>) {                            #每次读一行输入

  chomp;
  if (/YOUR_PATTERN_GOES_HERE/)    {
   print "Matched: |$`<$&>$'|\n";        #特殊匹配变量

} else {
    print "No match: |$_|\n";
}
}

习题:
1、
#!/usr/bin/perl -w

while (<STDIN>) {
  chomp;
  if (/(\b\w*a\b)/) {
  print "Matched: "$`<$&>$'|\n";
  print "\$1 contains '$1'\n";        #多输出一行
} else {
  print "No match: |$_|\n";
  }
}

========================================================
#!/usr/bin/perl -w
while (<STDIN>) {
  chomp;
  if (/(?<word>\b\w*a\b)/) {
  print "Matched: "$`<$&>$'|\n";
  print "'word' contains '$+{word}}'\n";        #多输出一行
} else {
  print "No match: |$_|\n";
  }
}
=========================================================
m!
  (\b\w*a\b)        #$1:某个以字母a结尾的英文单词
  (.{0,5})            #$2:后面接上的字符不超过5个
 !xs                #忽略空格和灵活的匹配空白的修饰符。
 
=========================================================
while (<>) {
  chomp;
  if (/\s+$/) {
    print "$_#\n";        ##井号(#)在这里用作标示,表示行尾的位置

    }
  }


相关阅读 更多 +
排行榜 更多 +
翌日波奇狗的历险记手机版下载

翌日波奇狗的历险记手机版下载

休闲益智 下载
怪兽远征安卓版下载

怪兽远征安卓版下载

角色扮演 下载
谷歌卫星地图免费版下载

谷歌卫星地图免费版下载

生活实用 下载