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 data\t may have extra whitespace.";
s/\s+/ /g; # 结果为 "Input data may have extra whitespace."

删除开头和结尾的空格
s/^\s+//;
s/\s+$//;

也可以写成:s/^\s+|\s+$//g;

不同的定界符

和m//与qw//一样,我们也可以改变s///的定界符。由于替换运算符要用到三个定界符,因此与先前的例子还有些不同,在使用成对出现的定界符是书写方式略有不同,如果不是成对出现的定界符就无所谓了,如下:

s#^https://#http://#;
s{fred}{barney};
s[fred](barney);
s<fred>#barney#;

第一个例子由于使用的不是成对出现的定界符,因此使用上与/差不多,后面几个例子使用的都是成对出现的定界符,因此在形式上有些不同,你甚至可以在一个替换模式中使用两种定界符!

可选修饰符

不仅是 /g 操作符,替换运算也可以使用我们经常在模式匹配中使用的 /i 、/x 与 /s 修饰符。它们使用时的顺序对结果没有影响。
s#wilma#Wilma#gi; # 将所有的 WiLmA 或 WILMA 替换为 Wilma
s{__END__.*}{}s; # 将__END__标记之后的所有内容都清除

绑定操作符

像之前m//时提到的,我们可以使用绑定操作符为s///选择不同的目标

$file_name =~ s#^.*/##s; # 将 $file_name变量中所有Unix风格路径全部去掉

大小写转换

这里介绍几个转义字符:
\U 将其后的所有字符转换为大写
\u 将其后的第一个字符转换为大写
\L 将其后所有的字符转为小写
\l 将其后的第一个字符转换为小写
\E 结束大小写转换的影响

示例:

$_ = "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$2\E with $1/i; # $_ 替换为 "I saw FRED with barney."
s/(fred|barney)/\u$1/ig; # $_ 替换后为 "I saw FRED with Barney."
s/(fred|barney)/\u\L$1/ig; # $_ 这里将首字母变为大写 "I saw Fred with Barney."

这里介绍的转义字符也适用于双引号包含的字符串中,如下:
print "Hello, \L\u$name\E, would you like to play a game?\n";

#!/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$2\E with $1/gi;

print "$_\n";

s/(barney|fred)/\u$1/gi;
print "$_\n";

s/(barney|fred)/\L\u$1/gi;
print "$_\n";

输出结果:
I saw BARNEY with FRED.
I saw barney with fred.
I saw FRED with barney.
I saw FRED with Barney.
I saw Fred with Barney.

Split操作符

split会根据分隔符拆分一个字符串,如果分割成功,返回的结果是按照分隔符切分的字段,否则返回空。

写法:@fields = split /separator/,$string;

任何匹配模式的内容都不会出现在返回的字段中。下面就是以冒号作为分隔符的典型split模式:
@fields = split /:/, "abc:def:g:h"; # 得到 ("abc", "def", "g", "h")

如果两个分隔符连在一起,会产生空字段:
@fields = split /:/, "abc:def::g:h"; # 得到 ("abc", "def", "", "g", "h")

split 还有一个不成文的规则:它会保留开头处的空字段,但却会省略结尾处的空字段。
@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+/, $_; 一致

Join函数

join函数不会使用模式,它的功能与split功能恰好相反,split会将字符串分解为数个子字符串,而join会把这些子字符串合并为一个字符串。用法如下:
my $result = join $glue,@pieces;
join函数的第一个参数是各个字符的分隔符,第二个参数就是需要合并的数组。
my $x = join ":",4,5,6,7,8,9,10,11;
结果$x为"4:5:6:7:8:9:10:11"

join与split的用法类似,但要记住join的第一个参数是字符串而非模式!

列表上下文中的m//

在列表上下文中使用的模式匹配操作符(m//)时,如果模式匹配成功,那么返回的是所有捕获变量的列表;如果匹配失败,则返回空列表:

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

输出结果: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: @words\n";

输出结果: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 => $value\n";
}

输出结果:
e => edit
a => apple
d => dog
c => carry
h => height
b => banany
g => good
f => fllow
i => idle
注意:本例中的模式匹配中一定要加上修饰符/g 否则就会匹配字符串中一次,这样的话哈希中也就只有一对值。如果加上/g的话就会对整个字符串进行全面的扫描了,从而将字符串完整的转换为哈希。

非贪婪量词

到目前为止我们碰到的4个量词({} + * ?)都是贪婪量词。也就是说在保证整体匹配的前提下,它们会尽量匹配长字符串。与之相对的就是非贪婪量词。对于每一个贪婪的量词,都有一个非贪婪的量词。以加号(+)为例,它的非贪婪量词为(+?),这表示一次或更多匹配(加号的本意)。但是匹配的字符串却是越短越好,而不是越长越好。举例来说:假如我想处理一个HTML文本,需要将去除<BOLD>跟</BOLD>这样的标记,并保留剩余的内容。如果要处理的字符串是这样:
I’m 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>,把这中间的部分全部取出来。这就错了,在这里我们就需要使用非贪婪量词。非贪婪版本的星号是*?,所以新的替换表达式应该写成这样:
s#<BOLD>(.*?)</BOLD>#$1#g;
这样就正确无误了!

看到这里你也就知道了剩下的两个量词的非贪婪版本了:
?? 虽然问号已经是匹配零次或一次了,但是这样写的话会优先考虑零次
{5,10}? 优先考虑5次

跨行的模式匹配

传统的正则表达式都是用来匹配单行文本。由于Perl可以处理任意长度的字符串,其模式匹配也可以处理多行文本,与处理单行文本并无差异。看看下面可以表示4行的文本:
$_ = "I'm much better\nthan Barney is\nat bowling,\nWilma.\n";
^和$通常是用来匹配整个字符串的开始和结束的。但当模式加上/m修饰符后,就可以让它们也匹配串内的换行符,这样一来,它们所代表的位置就不再是整个字符串的头尾,而是每行的开头跟结尾。因此,下面的模式就成立了:
print "Found 'wilma' at start of line\n" if /^wilma\b/im;

同样,也可以对多行文本逐个进行替换,参见下面的例子,先把整个文件读进一个变量,然后把文件名前置于每一行的开头:

#!/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/\0/gi;
s/wilma/Fred/gi;
s/\0/Willma/g;
print OUT $_;
}

注意:这里的替换是不能用正则表达式一步完成的,必须用一个中间量替换一下,然后再替换回来。本例使用\0也就是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;
}

Perl笔记:08、用正则表达式处理文…相关推荐

  1. JAVA自学笔记08

    JAVA自学笔记08 1.构造方法私有,外界就不能再创建对象 2.说明书的制作过程 1)写一个工具类,在同一文件夹下,测试类需要用到工具类,系统将自动编译工具类:工具类的成员方法一般是静态的,因此在测 ...

  2. 《编程之美》读书笔记08:2.9 Fibonacci序列

    <编程之美>读书笔记08:2.9 Fibonacci序列 计算Fibonacci序列最直接的方法就是利用递推公式 F(n+2)=F(n+1)+F(n).而用通项公式来求解是错误的,用浮点数 ...

  3. Spring-学习笔记08【面向切面编程AOP】

    Java后端 学习路线 笔记汇总表[黑马程序员] Spring-学习笔记01[Spring框架简介][day01] Spring-学习笔记02[程序间耦合] Spring-学习笔记03[Spring的 ...

  4. MyBatis-学习笔记08【08.动态SQL】

    Java后端 学习路线 笔记汇总表[黑马程序员] MyBatis-学习笔记01[01.Mybatis课程介绍及环境搭建][day01] MyBatis-学习笔记02[02.Mybatis入门案例] M ...

  5. JavaWeb黑马旅游网-学习笔记08【旅游线路详情】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb黑马旅游网-学习笔记01[准备工作] JavaWeb黑马旅游网-学习笔记02[注册功能] JavaWeb黑马旅游网-学习笔记03[登陆和 ...

  6. 二级VB培训笔记08:公共基础知识

    二级VB培训笔记08:公共基础知识 一.公共基础思维导图

  7. 正面管教读书笔记 08 班会

    正面管教读书笔记 08 班会 正面管教 作者:简·尼尔森(Jane Nelsen) 第8章 班会 这个章节非常有趣.在中国班会的作用不大.而作者认为班会,对于孩子来说,是一个非常好的学习和锻炼机会. ...

  8. Liunx学习笔记 - 07 - 02 正则表达式与文件格式化处理

    Liunx学习笔记 - 07 - 02 正则表达式与文件格式化处理 1 前言:啥是正则表达式 简单来讲,正则表达式是处理字符串的方法,它是以行为单位来进行字符串的处理行为,正则表达式通过一些特殊符号的 ...

  9. 【计算机网络学习笔记08】ICMP

    [计算机网络学习笔记08]ICMP 1 概念 由[RFC 792]定义的因特网控制报文协议(Internet Control Message Protocol,ICMP),被主机和路由器用来彼此沟通的 ...

  10. 推荐系统实践读书笔记-08评分预测问题

    推荐系统实践读书笔记-08评分预测问题 本书到目前为止都是在讨论TopN推荐,即给定一个用户,如何给他生成一个长度为N的推荐列表,使该推荐列表能够尽量满足用户的兴趣和需求.本书之所以如此重视TopN推 ...

最新文章

  1. 将HLSL射线追踪到Vulkan
  2. 007 Android之Broadcast Receiver
  3. Django项目工程配置工程日志
  4. 用户画像:数据指标与表结构设计
  5. 雷军 1994 年写的代码,经典老古董。
  6. 《Sibelius 脚本程序设计》连载(十四) - 2.1 注释、语句、语句块
  7. ListView控件使用简介(转载)
  8. QT实现“摇摇乐抽奖”(Lottery和Lottery2)
  9. REFPROP+matlab拟合物性参数公式
  10. sql问题导致CPU使用率100%
  11. Video Classification with Channel-Separated Convolutional Netwroks 论文阅读
  12. 鑫迪自助建站系统1.1版网站源码完整源码
  13. 所属学院mid函数计算机学院,EXCEL答案公式==
  14. XMind2TestCase 库的使用及自定义导出文档的格式
  15. 聊聊我当面试官的感受吧
  16. 程序员必备的几款文档编辑器
  17. 搜狗号正式上线广告收益功能!
  18. wire和reg的区别
  19. 单选/复选框中点击文字能选择该选项
  20. PyCharm远程开发调试

热门文章

  1. Axure,Justinmind以及Mockplus的交互设置方式对比
  2. 在电脑上收听广播——龙卷风网络收音机试用
  3. HTTP中GET,POST和PUT的区别
  4. CentOS 8 配置 authorized_keys 免密登录后,免密登录失败的排查以及最终的解决
  5. 职场菜鸟捕食指北【相亲篇】
  6. 让360俯首称臣----详细教程含源码
  7. Web安全之常见面试题总结
  8. win10无限蓝屏_WIN10无限重启怎么解决,现在开不了机
  9. 记录Notepad软件保护眼睛的颜色怎么设置
  10. mongodb分片集群数据库安全认证