Chinaunix首页 | 论坛 | 博客
  • 博客访问: 564356
  • 博文数量: 201
  • 博客积分: 7734
  • 博客等级: 少将
  • 技术积分: 1994
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-09 19:18
文章分类

全部博文(201)

文章存档

2011年(28)

2010年(173)

分类:

2010-08-21 23:54:52

可以通过正则表达式来改变文本。到目前为止,只介绍过怎么匹配模式。现在,我们将向你演示怎样通过模式来改变字符串中相应地部分。

一、使用s///进行替换

如果将 m// 这个模式匹配看作同文字处理器的 查询(search) 类似的功能,那Perl中 s/// 操作的则类似于查询并替换(search and replace) 。它将替换变量中模式所匹配上的部分。

$_ ="我喜欢打球"; s/很喜欢/不喜欢/; #很喜欢被不喜欢替换掉 print "$_\n";

如果没有匹配上,则什么也不会发生,此变量也不会有任何更改。

s///会返回一个Boolean 值。如果成功替换则返回true;否则返回false。

1.使用 /g 进行全局替换

在前面的例子中你可能已经注意到,s///值进行一次替换,无论是否还有地方还能匹配上。当然,这只是默认的行为。修饰符 /g 要求s///将不相重叠的所有匹配上的部分都进行替换。

$_ = "哇,好大的棉花糖哇!"; s/哇/哟/g; print "$_";

全局替换的一个常用地方是将多个空格用单个空格替换掉。

2.不同的分隔符

如同m//和qw//一样,我们也可以改变s///的分隔符。但这里使用了3个分隔符,因此有些不同。
通常的(非配对的)字符,由于没有左字符和右字符的区别,则可以像使用正斜线(/)那样使用。

如果使用的是配对的字符,也就是说其左字符和右字符不的,则必需使用两对:一对存放模式,一对存放替换的字符串。分隔符甚至可以是不同的。

s{fred}{barney}; s[fred](barney); s{fred}#barney#;

3.可选的修饰符

除了/g 修饰符外,替换操作中还可以使用/i, /x, 和/s,这些在普通的模式匹配中已经出现过的修饰符。其顺序是无关紧要的。

4.绑定操作 =~

同m//一样,我们也可以通过使用绑定操作符改变s///的替换目标:

$file_name = '/usr/bin/perl'; $file_name =~ s{\/}{ }g; print $file_name;

5.大小写转换

有时希望确保被替换的字符串均是大写的。这在Perl中只需使用某些修饰符就能办到。\U 要求紧接着的均是大写:

$_ ="i love you."; s/(i|you)/\U$1/gi; print $_;

同样也可以要求后面的均为小写:\L。

$_ ="I love YOU."; s/(I|YOU)/\L$1/gi; print $_;

默认时,会影响到剩余的(替换的)字符串。可以使用\E来改变这种影响:

$_ ="I love you."; s/(\w+) love (\w+)/\U$2\E love $1\L/; print $_;

使用小写形式(\l 和\u),只作用于下一个字符:

$_ ="I love you"; s/(I|you)/\u$1/ig; print $_;

也可以同时使用它们。如使用\u 和\L 表示第一个字母大写,其它字母均小写:

$_ ="i love you"; s/(i|you)/\u\L$1/ig; print $_;

NOTE:这些在替换中出现的大小写转换的修饰符,也可在双引号中使用。

二、split 操作

另一个使用正则表达式的操作是split,它根据某个模式将字符串分割开,保存到一个列表中。这对于由制表符分割开,冒号分割开,空白分割开,或者任意字符分割开的数据是非常有用的。任何可在正则表达式之中指定分离符(separator)的地方,均可用split。

split将模式同字符串进行比较,将由分离符所分隔开的子串作为列表返回回来。当模式匹配上,既是此次匹配结束,和新匹配的开始。因此,任何匹配模式的部分均不会在返回值中出现。

@fields = split/:/, "abc:def:g:h"; foreach $value (@fields) { print $value."\n"; }

三、join函数

join函数不使用模式,但它完成同split相反的操作:split将一个字符串分割开,而join函数将这些分割的部分组合成一个整体。


my $result = join $glue, @pieces;

join函数的第一个参数是粘合元素(glue),它可以是任意字符串。剩下的参数是要被粘合的部分,join将粘合元素添加在这些部分之间。

my $x = join("|", 4,6,8,10,12); print $x;

NOTE:如果列表中元素个数小于2,则不会有粘合的元素。

split和join可以一起使用,但不要忘了join的第一个参数是字符串,而非模式。

四、列表上下文中的m//

当使用split时,模式指定了分离符:这一部分不是有用的数据。有时指定要保留的部分更容易。
在列表上下文中使用模式匹配(m//)时,如果匹配成功返回值为内存变量值的列表,如果匹配失败则为空列表.

$_ ="I love perl"; my($first, $second, $third) =/(\S+) (\S+) (\S+)/; print "$third is my $second\n";

这种方法使我们可以给这些匹配的变量以合适的名字,这些值不会由于下次模式匹配而被覆盖(由于代码中没有=~,模式会自动和$_进行匹配)。

如果有不止一对括号,每一次返回不止一个字符串。例如将字符串放入hash中:

my $data = "Barney Rubble Fred FlintstoneWilma Flintstone"; my %last_name = ($data =~ / (\w+)\S+(\w+)/g); foreach $values (keys %last_name) { print "$last_name{$values} => $values \n"; }

每当模式匹配成功时,将返回一对值。这些一对一对的值就成了hash中的key/value对。

五、更强大的正则表达式

1.非贪婪的数量词

已经出现过的四个数量词(*,+,?,{})均是贪婪的。这就是说它们将最大限度的匹配,而非在匹配成功时即立刻返回。

NOTE:正则表达式引擎有大量的回退(backtracking)操作,尝试每一种可能,直到成功或者根本不能匹配为止。

因此对于每一个贪婪的数量词,需要一种非贪婪的方法。不是使用加号(+),而是使用非贪婪的数量词+?,它将匹配一次或多次(加号的意思),但其匹配尽可能少的次数,而非尽可能多的次数。

非贪婪数量词不仅和效率相关。即便它和其对应的贪婪数量词表达式均能匹配(或者不能匹配)同一个字符串,它们匹配的部分也可能是不同的。

如:取出span中的内容

$_ = "我在马路边捡到一分钱哇哈哈,如果再多一个哇哈哈会怎么样!"; s/\(.*)\<\/b\>/$1/g; print $_;  

其问题出在星号是贪婪的。

$_ = "我在马路边捡到一分钱哇哈哈,如果再多一个哇哈哈会怎么样!"; s/\(.*?)\<\/b\>/$1/g; print $_;  

由于加号的非贪婪类型是+?,星号的为*?,你可能已经意识到剩下的两种数量词其对应的类型也是类似的。花括号的非贪婪类型看起来一样,只是在闭花括号后有一个问号,如{5,10}?或者{8,}?。甚至问号数量词也有非贪婪类型:??。它匹配一次或者0次,但倾向于匹配0次。

2.匹配多行文本

通常,正则表达式是针对单行文本的。由于Perl可以处理任意长度的字符串,因此,Perl的模式可以轻易的对多行文本进行匹配,就像单行文本一样。当然,表达式中应当包含多行文本。

锚定^和$是指整个字符串的开头和结束。但/m 这个正则表达式选项允许它们根据内部的换行符进行匹配,将m看作多行(think m for multiple lines)。这时锚定针对每一行,而非整个字符串。

$_ ="我有多行\n第二行\n第三行\n"; print "输出成功" if /^我有.*/m;

3.非捕捉用的括号

Perl的正则表达式有一种方法可以使括号只进行分组,而不会引起内存变量的分配。我们将它叫做非捕捉用的括号(non-capturing parentheses),对于它,有一个特殊的写法。我们在开括号后面加上一个问号和冒号(?:),其作用事告诉Perl括号只是分组的作用。

$_ = '我的测试 括号只是用来分组 不是用来存储到内存中'; if (/(?:我的测试)?(括号只是用来分组|不是用来存储到内存中)/) { print "$1"; }

总结:Perl的正则表达式的括号还有些其它的特殊用法,它们可以完成某些复杂的功能,如向前找,向后找,内嵌注释,甚至在模式中执行代码。

阅读(1394) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~