Perl笔记:08、用正则表达式处理文…
1、用s///替换
$_
=
"He's out bowling with Barney tonight."
;
s/Barney/Fred/ ; # 将 Barney 替换为 Fred print "$_n" ; 当然这里可以使用比较复杂的正则表达式
s
/with
(
w
+
)
/against
$1
's team/;
print "$_n"; 输出He's out bowling against Fred's team tonight. 再来看看更多的示例:
$_
=
"green scaly dinosaur"
;
s/(w+) (w+)/$2,$1/ ; # 替换后为 "scaly,green dinosaur" s/^/huge,/ ; # 替换后为"huge,scaly,green dinosaur" s/,.*een// ; # 将模式内容清空后为 "huge dinosaur" s/green/red/ ; # 匹配失败,结果仍为 "huge dinosaur" s/w+$/($`!)$&/ ; # 替换后为 "huge (huge !)dinosaur" s/s+(!W+)/$1 / ; # 替换后为 "huge (huge!) dinosaur" s/huge/gigantic/ ; # 替换后为 "gigantic (huge!) dinosaur" s///返回的是布尔值,替换成功为真,否则为假。 使用/g进行全局替换s///的模式只会替换到第一个匹配的结果,但如果在后面加上一个/g情况就发生了变化,它会替换所有匹配的内容。 示例:
$_
=
"home,sweet home!"
;
s/home/cave/g ; print "$_n" ; # "cave,sweet cave!" 常见的替换应用是将多个空白替换为一个空格,如下:
$_
=
"Input datat may have extra whitespace."
;
s/s+/ /g ; # 结果为 "Input data may have extra whitespace." 删除开头和结尾的空格 不同的定界符和m//与qw//一样,我们也可以改变s///的定界符。由于替换运算符要用到三个定界符,因此与先前的例子还有些不同,在使用成对出现的定界符是书写方式略有不同,如果不是成对出现的定界符就无所谓了,如下:
s
#^https://#http://#;
s {fred } {barney } ; s [fred ] (barney ) ; s <fred> #barney#; 第一个例子由于使用的不是成对出现的定界符,因此使用上与/差不多,后面几个例子使用的都是成对出现的定界符,因此在形式上有些不同,你甚至可以在一个替换模式中使用两种定界符! 可选修饰符不仅是 /g 操作符,替换运算也可以使用我们经常在模式匹配中使用的 /i 、/x 与 /s 修饰符。它们使用时的顺序对结果没有影响。 绑定操作符 像之前m//时提到的,我们可以使用绑定操作符为s///选择不同的目标
大小写转换 这里介绍几个转义字符: 示例: ?
$_
=
"I saw Barney with Fred."
;
s/(fred|barney)/U$1/gi ; # $_ 现在成了 "I saw BARNEY with FRED." s/(fred|barney)/L$1/gi ; # $_ 现在成了 "I saw barney with fred." s/(w+) with (w+)/U$2E with $1/i ; # $_ 替换为 "I saw FRED with barney." s/(fred|barney)/u$1/ig ; # $_ 替换后为 "I saw FRED with Barney." s/(fred|barney)/uL$1/ig ; # $_ 这里将首字母变为大写 "I saw Fred with Barney." 这里介绍的转义字符也适用于双引号包含的字符串中,如下:
#!/usr/bin/perl -w
use strict ; $_ = "I saw Barney with Fred." ; s/(barney|fred)/U$1/gi ; print "$_ n" ; s/(barney|fred)/L$1/gi ; print "$_n" ; s/(w+) with (w+)/U$2E with $1/gi ; print "$_n" ; s/(barney|fred)/u$1/gi ; print "$_n" ; s/(barney|fred)/Lu$1/gi ; print "$_n" ; 输出结果: Split操作符split会根据分隔符拆分一个字符串,如果分割成功,返回的结果是按照分隔符切分的字段,否则返回空。 写法:@fields = split /separator/,$string; 任何匹配模式的内容都不会出现在返回的字段中。下面就是以冒号作为分隔符的典型split模式: 如果两个分隔符连在一起,会产生空字段: split 还有一个不成文的规则:它会保留开头处的空字段,但却会省略结尾处的空字段。 利用/s+/模式进行空白分隔也是常见的做法。在此模式下,所有的空白会被当成一个空格来处理:
my
$some_input
=
"This is a t test.n"
;
my @args = split /s+/ , $some_input ; # 得到 ("This","is","test.") # 上面的可以简写为下面的方式,因为split操作符默认是以空白分隔$_变量 my @fields = split ; # 与 split /s+/,$_; 一致 Join函数 join函数不会使用模式,它的功能与split功能恰好相反,split会将字符串分解为数个子字符串,而join会把这些子字符串合并为一个字符串。用法如下: join与split的用法类似,但要记住join的第一个参数是字符串而非模式! 列表上下文中的m//在列表上下文中使用的模式匹配操作符(m//)时,如果模式匹配成功,那么返回的是所有捕获变量的列表;如果匹配失败,则返回空列表:
$_
=
"Hello there,neighbor!"
;
my ( $first , $second , $third ) = /(S+) (S+),(S+)/ ; print "$second is my $thirdn" ; 输出结果:there is my neighbor! 先前已经在s///的例子中用到了/g修饰符,该修饰符同样也可以用在m//操作符上,其效果就是让模式能够匹配到字符串中的许多地方。下面的例子中具有一对圆括号的模式,它会在每次匹配成功时返回一个捕获串:
my
$text
=
"Fred dropped a 5 ton granite block on Mr. Slate"
;
my @words = ( $text =~ /([a-z]+)/ig ) ; print "Result: @wordsn" ; 输出结果:Result: Fred dropped a ton granite block on Mr Slate 这就相当于自己动手实现了split的功能,但并不是指定想要去除的部分(split中的第一个参数,也即是分隔符实际上就是要去除的部分),反而是指定想要流行的部分。 如果模式中有多对圆括号,那么每次匹配就能捕获多个串。假设,我想让一个字符串变成哈希,可以这样做:
#!/usr/bin/perl -w
use strict ; my $letter2name = "a apple b banana c carry d dog e edit f fllow g good h height i idle" ; my %newhash = ( $letter2name =~ /(w+)s+(w+)/g ) ; while ( my ( $key , $value ) = each %newhash ) { print "$key => $valuen" ; } 输出结果: 非贪婪量词到目前为止我们碰到的4个量词({} + * ?)都是贪婪量词。也就是说在保证整体匹配的前提下,它们会尽量匹配长字符串。与之相对的就是非贪婪量词。对于每一个贪婪的量词,都有一个非贪婪的量词。以加号(+)为例,它的非贪婪量词为(+?),这表示一次或更多匹配(加号的本意)。但是匹配的字符串却是越短越好,而不是越长越好。举例来说:假如我想处理一个HTML文本,需要将去除<BOLD>跟</BOLD>这样的标记,并保留剩余的内容。如果要处理的字符串是这样: 看到这里你也就知道了剩下的两个量词的非贪婪版本了: 跨行的模式匹配 传统的正则表达式都是用来匹配单行文本。由于Perl可以处理任意长度的字符串,其模式匹配也可以处理多行文本,与处理单行文本并无差异。看看下面可以表示4行的文本: 同样,也可以对多行文本逐个进行替换,参见下面的例子,先把整个文件读进一个变量,然后把文件名前置于每一行的开头:
#!/usr/bin/perl -w
use strict ; my $filename = "test.log" ; open MAILLOG , $filename or die "Can't open $filename:$!" ; my $line = join '' , <MAILLOG> ; $line =~ s/^/$filename : /gm ; print "$line" ; 一次更新多个文件要在Perl中直接修改文件内容可以使用钻石操作符(<>),先看下面的例子:
#!/usr/bin/perl -w
use strict ; chomp ( my $date = `date` ) ; $^I = ".bak" ; while ( <> ) { s/^Author:.*/Author: Randal L. Schwartz/ ; s/^Phone:.*n// ; s/^Date:.*/Date: $date/ ; print ; } 程序一开始使用了系统的date命令,下一行则是对$^I变量的赋值,这个赋值是个扩展名,也就是再修改的时候要先备份一下,以防万一。 ===================本章习题====================== 1、写个程序来赋值并修改指定的文本文件。在副本里,此程序会把出现字符串Fred(大小写不计)的每一处都换成Larry。输入文件名应该在命令行上指定,输出文件名是本来的文件名加上.out。
#!/usr/bin/perl -w
use strict ; my $in = $ARGV [ 0 ] ; unless ( defined $in ) { die "Usage $0 filename" ; } my $out = $in ; $out =~ ( s / ( . w ) ? $/ .out / ) ; unless ( open IN , "<$in" ) { die "Open file $in error:$?" ; } unless ( open OUT , ">$out" ) { die "Write file $out error:$?" ; } while ( <IN> ) { s/fred/Larry/gi ; print OUT $_ ; } 2、修改前一题的程序,以便把所有的Fred换成Wilma并把所有的Wilma换成Fred。如果输入的是fred&wilma,那么正确的输出应是Wilma&Fred。
#!/usr/bin/perl -w
use strict ; my $in = $ARGV [ 0 ] ; unless ( defined $in ) { die "Usage $0 filename" ; } my $out = $in ; $out =~ ( s / ( . w ) ? $/ .out / ) ; open IN , "<$in" or die "Open file $in error:$?" ; unless ( open OUT , ">$out" ) { die "Write file $out error:$?" ; } while ( <IN> ) { s/fred/ /gi ; s/wilma/Fred/gi ; s/ /Willma/g ; print OUT $_ ; } 注意:这里的替换是不能用正则表达式一步完成的,必须用一个中间量替换一下,然后再替换回来。本例使用 也就是NULL作为中间量。 3、写个程序,把你目前写过的所有程序都加上版权声明,也就是在第一行的后面加上如下信息:## Copyright (C) 20XX by Yours Truly。你应该直接修改修改文件内容并且做备份。假设你将在命令行指定待修改的文件名。
#!/usr/bin/perl -w
use strict ; $^I = ".bak" ; while ( <> ) { if ( /^#!/ ) { $_ .= "## Copyright (C) 2010 by Cooper!n" } print ; } 4、修改前一题程序里的模式,如果文件中已经有版权声明,就不在进行修改。提示:你可能需要知道钻石操作符当前正在读取的文件名称,可以在$ARGV里找到。 为了避免重复加上版权声明,我们得分两次处理文件。第一次,我们需要先建立一个哈希,它的键是文件名称,而它的值是什么无关紧要,为了安全起见,此处将值置为1:
my
%file
;
foreach ( @ ARGV ) { $file { $_ } = 1 ; } 第二次,我们会吧这个哈希当成待办列表逐个处理,并把已经包含版权声明的文件移除。目前正在读取的文件名称可用$ARGV获取,所以可以直接把它拿来当哈希键:
while
(
<>
)
{
if ( /^## Copyright/i ) { delete $file ( $ARGV ) ; } } 最后的部分跟之前所写的程序一样,但我们会会事先把@ARGV的内容改掉:
@
ARGV
=
sort
keys
%file
;
$^I = ".bak" ; while ( <> ) { if ( /^#!/ ) { $_ .= "## Copyright (C) 2010 by Cooper!n" } print ; } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |