分类:
2009-07-23 13:49:18
可以通过正则表达式来改变文本。到目前为止,只介绍过怎么匹配模式。现在,我们将向你演示怎样通过模式来改变字符串中相应地部分。 一、使用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函数将这些分割的部分组合成一个整体。
join函数的第一个参数是粘合元素(glue),它可以是任意字符串。剩下的参数是要被粘合的部分,join将粘合元素添加在这些部分之间。 my $x = join("|", 4,6,8,10,12); print $x; NOTE:如果列表中元素个数小于2,则不会有粘合的元素。 split和join可以一起使用,但不要忘了join的第一个参数是字符串,而非模式。 四、列表上下文中的m// 当使用split时,模式指定了分离符:这一部分不是有用的数据。有时指定要保留的部分更容易。 $_ ="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的正则表达式的括号还有些其它的特殊用法,它们可以完成某些复杂的功能,如向前找,向后找,内嵌注释,甚至在模式中执行代码。 |