使用正则表达式处理文件

1.使用s///进行替换

$_ =“He’s out bowling with Barney tonight.”;
s/Barney/Fred/; #Barney 被Fred 替换掉
print “$_\n”

如果没有匹配上,则什么也不会发生,此变量也不会有任何更改:
#接上例:现在$_ 为“He’s out bowling with Fred tonight.”
s/Wilma/Betty/; #用Wilma 替换Betty(失败)

模式和被替换的字符串可能更复杂。下例中,替换的字符串使用了变量:$1,其值由模式匹配所赋值:
s/with (\w+)/agaist $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///会返回一个Boolean 值。如果成功替换则返回true;否则返回false:
$_ = “fred flintstone”;
if(s/fred/wilma/){
print “Successfully replaced fred with wilma!\n”;
}


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

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

$_ = “home, sweet home!”;
s/home/cave/g;
print “$_\n”; # “cave, sweet cave!”

全局替换的一个常用地方是将多个空格用单个空格替换掉:
$_ =“Input data\t may have extra whitespace.”;
s/\s+/ /g; #现在是“Input data may have extra whitespace.”

现在已经知道怎样去掉多余的空格,那怎样去掉开头和结尾的空白呢?这是非常容易的:
s/^\s+//; #将开头的空白去掉

s/\s+$//; #将结尾的空白去掉

s/^\s+|\s+$//g; #将开头,结尾的空白去掉

1.2 不同的分隔符

s#^https://#http://#;


如果使用的是配对的字符,也就是说其左字符和右字符不的,则必需使用两对:一对存放模式,一对存放替换的字符串。
此时,分隔符甚至可以是不同的。事实上,分隔符还可以使用普通的符号(非配对的)。下面三例是等价的:

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

1.3 可选的修饰符

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

s#wilma#Wilma#gi; #将所有的WilmA,或者WILMA 等等,由Wilma 替换掉
s{_ _END_ _.*}{ }s; #将END 标记及其后面的行去掉

1.4绑定操作

$file_name =~ s#^.*###s; #将$file_name 中所有的Unix 类型的路径去掉


1.5 大小写转换

有时,希望确保被替换的字符串均是大写的(或者不是,视情况而定)。这在Perl 中只需使用某些修饰符就能办到。\U 要
求紧接着的均是大写:

$_ =“I saw Barney with Fred.”;
s/(fred|barney)/\U$1/gi; #$_现在是“I saw BARNEY with FRED.”

同样,也可以要求后面的均为小写:\L:
s/(fred)|barney/\L$1/gi; #$_现在是“I saw barney with fred.”

使用小写形式时(\l 和\u),只作用于下一个字符:
s/ (fred|barney)/\u$1/ig; #$_现在是“I saw FRED with Barney.”
也可以同时使用它们。如使用\u 和\L 表示“第一个字母大写,其它字母均小写”◆:

s/(fred|barney)/\u\L$1/ig; #$_现在为“I saw Fred with Barney.”


2.split 操作

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

@fields = split /separtor/, $string;

@fields = split /:/, “abc:def:g:h”; #返回(“abc”, “def”, “g”, “h”)

可能得到空的元素,如果其中有两个分隔符是连在一起的:

这里有一条规则:开头的空元素会被返回,但结尾的空元素被丢弃◆。虽然第一次见到时看起来有些古怪,但很少引起问
题。
@fields = split /:/, “:::a:b:c:::”; #得到(“”, “”, “”, “a”, “b”, “c”);

使用空白/\s+/这个模式进行分割是非常常见的。在这个模式下,所有的空白等价于单个空格:
my $some_input = “This is a \t test.\n”;
my @args = split /\s+/, $some_input; #(“This”, “is”, “a”, “test.”)

默认时,split 对$_操作,模式为空白:
my @fields = split; #同split /\s+/, $_;


3.join 函数

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

my $result = join $glue, @pieces;

my $x = join“:”, 4, 6, 8, 10, 12; #$x 为“4:6:8:10:12”

如果列表中元素个数小于2,则不会有粘合的元素:
my $y = join “foo”, “bar”; #得到“bar”
my @empty; #空数组
my $empty = join “baz”, @empty; #没有元素,因此为空串

my @values = split /:/, $x; #@values 为(4, 6, 8, 10, 12)my $z =join “-”, @values; #$z 为“4-6-8-10-12”


4.列表上下文中的m//

在列表context 中使用模式匹配(m//)时,如果匹配成功返回值为内存变量值的列表;如果匹配失败则为空列表:

$_ =“Hello there, neighbor!”;
my($first, $second, $third) =/(\S+) (\S+), (\S+)/;
print “$second is my $third\n”;

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


在s///中介绍的/g 修饰符也可在m//中使用,它允许你在字符串中的多处进行匹配。在这里,由括号括起来的模式将在每一
次匹配成功时返回其内存中所存放的值:

my $text = “Fred dropped a 5 ton granite block on Mr. Slate”;
my @words = ($text =~ /([a-z]+)/ig);
print “Result: @words\n”;
#Result: Fred dropped a ton granite block on Mr slate

如果有不止一对括号,每一次返回不止一个字符串。例如将字符串放入hash 中,如下:
my $data = "Barney Rubble Fred Flintstone Wilma Flintstone";
my %last_name = ($data =~ /(\w+) (\w+)/g);
print keys(%last_name);
每当模式匹配成功时,将返回一对值。这些一对一对的值就成了hash 中的key/value 对。


5.更强大的正则表达式

5.1非贪婪的数量词

假设在fred and barney went bowling last night 上使用/fred.+barney/进行匹配。我们知道正则表达式
将匹配上,下面我们具体的讲解这一个过程◆。首先,子模式fred 将匹配其对应的字符串。模式的下一部分是.+,它将匹
配除了换行符之外的任意字符,次数大于等于一。。但,由于加号(+)是贪婪的;它将尽可能的进行匹配。因此,它将匹配
剩余的所有字符串,包括night。这可能让你惊奇,但故事还没结束。)

现在对banrey 进行匹配,但不能成功,因为已经到了字符串的结尾处。由于.+在少一个字符的情况下仍能匹配成功,因此
它退回字符串最后一个字母t。(它虽是贪婪的,但更希望整个模式能匹配成功。)子模式barney 又尝试匹配,结果仍是不行。

因此.+再退回字母h,又进行匹配。一个字符接一个字符,.+退回其匹配的字
符,直到其退回了字符串barney。最后,子模式banrey 被匹配上了,现在整个模式都匹配上了。



因此对于每一个贪婪的数量词,需要一种非贪婪的方法。不是使用加号(+),而是使用非贪婪的数量词+?,它将匹配一次或
多次(加号的意思),但其匹配尽可能少的次数,而非尽可能多的次数。现在我们来看看模式为/fred.+?barney/时的过程。

首先,fred 将被匹配上。接着,模式的下一部分是.+?,它匹配的字符个数不大于1,因此匹配fred 后面的空格。下一个子
模式是banrey,它在这里不能被匹配(因为现在的位置是and barney… 的开头)。.+?再匹配a,剩下的模式继续进行匹配。
又一次,barney 不能匹配上,因此.+?再匹配n,依次类推。当.+?匹配了这5 个字符后,barney 可以被匹配上了,现在模
式匹配成功。


这里也存在一些回退操作,但由于引擎只需回退,并只尝试几次,其在速度上会有很大提高。但,这种提高依赖于banrey
在fred 的附近能被找到。如果数据中fred 在字符串的开头,而barney 在结尾处,则贪婪数量词方法的速度更快。因此,正
则表达式的速度依赖于具体的数据。



非贪婪数量词不仅和效率相关。即便它和其对应的贪婪数量词表达式均能匹配(或者不能匹配)同一个字符串,它们匹配
的部分也可能是不同的。例如,假设你有一些HTML 类型的◆文本,你想移除标记<BOLD>和</BOLD>,而保留其间的
内容。下面是文本:

I’am talking about the cartoon with Fred and <BOLD>Wilma</BOLD>!

下面是一种移除标记的方法。它有什么错误呢?
s#<BOLD>(.*)</BOLD>#$1#g;
其问题出在星号是贪婪的◆。如果文本变成了下面的样子,会得到什么结果?

I thought you said Fred and <BOLD>Velma</BOLD>, not <BOLD>Wilma</BOLD>

此时,模式将匹配从第一个<BOLD>到最后一个</BOLD>之间的内容,中间的部分被保留下来。噢!我们需要非贪婪的数
量词。星号的非贪婪的类型是*?,因此此模式应当是:
$#<BOLD>(.*?)</BOLD>#$1#g;
现在它能正确执行了。


5.2匹配多行文本

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

$_ =“I’am much better\nthan Barney is\nat bowling,\nWilma,\n”

锚定^和$是指整个字符串的开头和结束 。但/m 这个正则表达式选项允许它们根据内部的换行符进行匹配(将
m看作多行(think m for multiple lines))。这时锚定针对每一行,而非整个字符串。因此,这个模式可以匹配上:
print “Found ‘wilma’at start of line\n”if /^wilma\b/im;


同样的,在多行字符串中,也可以分别针对单行进行替换。在下面的例子中,我们将整个文件读入一个变量之中◆,然后

将文件名字加在每一行的开头处:

open FILE, $filename
or die “Can’t open ‘$filename’: $!”;
my $lines = join '', <FILE>;
$lines =~ s/^/$filename: /gm;










  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值