用s///进行替换:如果把m//模式匹配(pattern match)想象成文字处理器的“查找”功能,那么s///替换(substitution)操作符就是“查找并替换”功能。此操作符只是把存在变量中匹配模式的那部分内容替换成另一个字符串:
<span style="font-size:18px;">$_ = "He's out bowling with Barney tonigth."; s/Barney/Fred/; #replacing Barney with Fred print "$_\n";</span>
如果匹配失败,则什么事都不会发生,变量也不会受影响。当然,模式字符串与替换字符串还可以更加复杂。下面的替换字符串用到了第一个捕获的变量,也就是$1,模式匹配时会对它赋值:
<span style="font-size:18px;">s/with (\w+)/against $1's team/; print "$_\n"; #print “He’s out bowling against Fred’s team tonight.”</span>
S///返回的是布尔值,替换成功时为真,否则为假。
用/g进行全局替换,s///只会进行一次替换,/g修饰符可以让s///进行所有可能的,不重复的替换:
<span style="font-size:18px;">$_ = “home, sweet home !”; s/home/cave/g; print “$_\n”; #print “cave, sweet cave!”</span>
一个相当常见的全局替换就是缩减空白,也就是将任何连续的空白转换为单一空格:
<span style="font-size:18px;">$_ = “Input data\t may have extra whitespace.”; s/\s+/ /g; #”Input data may have extra whitespace.”</span>
大小写替换:\U转义符会将其后的所有字符转换成大写的:
<span style="font-size:18px;">$_ = “I saw Barney with Fred.”; s/(fred|barney)/\U$1/gi; # “I saw BARNEY with FRED.”</span>
类似地,\L转义符会将它后面的所有字符转换成小写的。默认情况下,它们会影响之后全部的(替换)字符串。你也可以用\E关闭大小写转换的功能:
<span style="font-size:18px;">s/(\w+) with (\w+)/\U$2\E with $1/i; #”I saw FRED with Barney.”</span>
split操作符,用法:my @fields = split /separator/, $string;
这里的split操作符用拆分模式扫描指定的字符串并返回字段(也就是子字符串)列表。期间只要模式在某处匹配成功,该处就是当前字段的结尾、下一个字段的开头。split会保留开头处的空字段,却会舍弃结尾处的空字段。
默认split会以空白符分隔$_中的字符串: my @fields = split; # split /\s+/, $_;
join函数不会使用模式,它的功能与split恰好相反。用法: my $result = join $glue, @pieces; 我们可以把join的第一个参数理解为胶水,它可以是任意字符串。其余参数则是一串片段。join会把胶水涂进每个片段之间并返回结果字符串:
my $x = join “:”, 4,6, 8, 10, 12; # $x为”4:6:8:10:12”
所以说,列表至少要有两个元素,否则胶水无法涂进去。
列表上下文中的m//,在使用split时,模式指定的只是分隔符:分解得到的字段未必就是我们需要的数据。有时候,指定想要留下的部分反而比较简单。之前在s///的例子中看到的/g修饰符同样也可以用在m//操作符上,其效果就是让模式能够匹配到字符串中的多个地方。
<span style="font-size:18px;">my $text = “Fred dropped a 5 ton granite block on Mr. Slate”; my @words = ($text =~/([a-z]+)/ig); print “Result: @words\n”; # print : Fred dropped a ton granite block on Mr Slate</span>
这就好比是反过来用split:正则模式指定的并非想要去除的部分,反而是要留下的部分。
事实上,如果模式中有多组圆括号,那么每次匹配就能捕获多个字符串。假设我们想把一个字符串变成哈希,就可以这样做:
my $data = “Barney Rubble Fred Flintstone Wilma Flintstone”; my %last_name = ($data =~ /(\w+)\s+(\w+)/g);
每次模式匹配成功就会返回一对被捕获的值,而这一对值正好成为新哈希的键值对。
非贪婪量词。正则表达式引擎一直进行回溯动作,不断地以不同的方式调整模式匹配的内容来适应字符串,直到最终找到一个整体匹配成功的为止,要是直到最后都找不到就宣告失败。
跨行的模式匹配, ^和$都是代表整个字符串的开头和结尾的锚位。但当模式加上/m修饰符之后,就可以用它们匹配字符串内的每一行,这样一来,它们代表的位置就不再是整个字符串的头尾,而是每行的开头跟结尾了。接下来的程序会先把整个文件读进一个变量,然后把文件名作为每一行的前缀进行替换:
<span style="font-size:18px;">$fliename = "hsl.txt"; open FILE, $fliename or die "Can't open '$fliename' : $!"; my $lines = join '',<FILE>; $lines =~s/^/$fliename: /gm; print $lines;</span>
一次更新多个文件:通过程序自动更新文件内容时,最常见的做法就是先打开一个和原来内容一致的新文件,然后在需要的位置进行改写,最后把修改后的内容写进去。
现在有个叫做fred03.dat的文件内容如下:
需要改为:
简单的说,我们需要作出三项改动:Author字段的姓名要改,Date要改为今天的日期,而Phone要删除。
<span style="font-size:18px;">#!/usr/bin/perl -w use strict; chomp(my $date = localtime); $^I = "f.bak"; while(<>){ s/^Author:.*/Author: Randal L. Schwartz/; s/^Phone:.*\n//; s/^Date:.*/Date: $date/; print; }</span>
在命令行中输入:perl change.pl fred03.dat
先假设钻石操作符正好打开了文件fred03.dat。除了像以前一样打开文件之外,他还会把文件名改为fred03.dat.bak。虽然打开的是同一个文件,但是它在磁盘上的文件名已经不同了。接着,钻石操作符会打开一个新文件并将它取名为fred03.dat。这么做并不会有任何问题,因为我们已经没有同名文件了。现在钻石操作符会把默认的输出设定为这个新打开的文件,所以输出来的所有内容都会被写进这个文件。
习题:
1.我的:
<span style="font-size:18px;">use strict; chomp(my $what = <STDIN>); print "$what" x 3;</span>
答案:/($what){3}/表示没有看懂
2
<span style="font-size:18px;">my $in = $ARGV[0]; if(! defined $in){ die "Usage:$0 filename"; } my $out = $in; $out = ~s/(\.\w+)?$/.out/; if(! open $in_fh, '<',$in){ die "Can't open '$in':$!"; } if(! open $out_fh, '>',$in){ die "Can't open '$out':$!"; } while(<$in_fh>){ s/Fred/Larry/gi; print $out_fh $_; }</span>
此程序一开始会先清点它的命令行参数,预期应该要一个。
3
<span style="font-size:18px;">my $in = $ARGV[0]; if(! defined $in){ die "Usage:$0 filename"; } my $out = $in; $out = ~s/(\.\w+)?$/.out/; if(! open $in_fh, '<',$in){ die "Can't open '$in':$!"; } if(! open $out_fh, '>',$in){ die "Can't open '$out':$!"; } while(<$in_fh>){ chomp; s/Fred/Larry/gi; s/Wilma/Fred/gi; s/\n/Wilma/g; print $out_fh “$_\n”; }</span>
我们必须先找到一个“占位符”,而且必须是不会出现在数据中的。因为使用了chomp,所以我们知道换行符(\n)是绝对不会出现在字符串中的,所以换行符就可以充当占位符。
4
<span style="font-size:18px;">$^I = ".bak"; while(<>){ if(/\A#!/){ $_.="##Copyright (C) 2015 by HSL\n"; } print; }</span>
5.为避免重复加上版权声明,我们得分两回处理所有文件。第一回,我们会先建立一个哈希,它的键是文件名称,而它的值是什么并不重要。为了简单起见,设为1。第二回,我们会把这个哈希当成待办事项列表逐个处理,并把已经包含版权声明行的文件移除。目前正在读取的文件名称可用$ARGV取得,所以可以直接把它拿来当哈希键。
<span style="font-size:18px;">my %do_these; foreach(@ARGV){ $do_these{$_} = 1; } while(<>){ if(/\A## Copyright/){ delete $do_these{$ARGV}; } } @ARGV = sort keys %do_these; $_I = ".bak"; while(<>){ if(/\A#!/){ $_ = "## Copyright (c) 2015 by HSL"; } print;</span>
这一章节让我更加深入地了解了如何用perl对文件进行操作。与此同时,我正在继续看我的VTR-to-Bitstream项目,其中在将Virtex-6改为ZYNQ遇到了不少的问题。不过正在和老师商讨如何攻克。也开始着手看一些关于可重构计算的书了。
版权声明:本文为博主原创文章,未经博主允许不得转载。