Learning Perl 学习笔记 Ch9 正则表达式(三)用正则表达式处理文本

  1. 重新理解正则表达式,Perl中的正则表达式其实不是独立的功能,只是内嵌于某些功能中的子语言, m//就像是文本编辑器中的搜索功能,Perl同样提供了替换功能:s/正则表达式的模式/替换的内容/,如果没有用=~绑定对象,则默认替换的是预置变量$_
    demo9-1:
#!/usr/bin/perl
$_="This is a test.\n";
if($result = s/is/was/){
  print $_;
}
print "result:$result";

作为表达式,s///返回匹配结果的布尔值:

./demo9-1
Thwas is a test.
result:1

  • s///默认只会替换第一个匹配的位置,如果要实现全部替换,需要修饰符/g
    demo9-2:
#!/usr/bin/perl
$_="This is a test.\n";
s/is/was/g;
print $_;
 ./demo9-2
Thwas was a test.

m//中的修饰符在s///中同样可以使用,包括:
/i:忽略大小写
/s:让.匹配包括换行符在内的任意字符
/x:允许在模式中插入任意空白和换行


  • m//一样,在s///中也可以使用捕获变量
    demo9-3:
#!/usr/bin/perl
$_="This is a test.\n";
s/(\w+) (\w+) (\w+) (\w+)/$4 $3 $2 $1/;
print $_;

把捕获的变量逆序输出

./demo9-3
test a is This.

我们也可以把反向引用加进去:
demo9-4:

#!/usr/bin/perl
$_="This is a test.\n";
s/(is) \1/were/;
print $_;

匹配到is is

 ./demo9-4
Thwere a test.

命名捕获变量和自动捕获变量同样可以使用:
demo9-5:

#!/usr/bin/perl
$_="This is a test.\n";
s/(?<be>is)/_$+{be}_/;
print $`."\n";
print $&."\n";
print $'."\n";
print $_;
./demo9-5
Th
is
 is a test.

Th_is_ is a test.

  • s///的定界符可以替换,如果是非成对定界符(比如#)只需要重复三次来分割圈引模式和圈引替换模式,但如果是成对定界符(比如{}[])就需要成对使用两次来区分圈引模式和圈引替换模式:
    s#abc#def#
    s{abc}[def]
    虽然 #也被用来表示注释, 但如果Perl解析器在期待一个定界符,就不会把 #视作注释的开头)

  • s///的圈引替换模式中,有一组特殊的转义字符可以实现大小写转换:
    \U:之后的字符全部转换成大写
    \L:之后的字符全部转换为小写
    \E:结束\U\L的作用范围
    \u:之后的第一个字符大写
    \l:之后的第一个字符小写
    可以将\u\L或者\l\U连用实现首字母大写或首字母小写的功能:
    demo9-6
#!/usr/bin/perl
$_="This is a test.\n";
print $_;
s/(?<be>test)/\u\L$+{be}/;
print $_;
./demo9-6
This is a test.
This is a Test.

大小写转义字符的规则并不是s///专属的,任何双引号内的字符串都适用此规则
demo9-7

#!/usr/bin/perl
$_="This is a \u\Ltest.\n";
print $_;
s/(?<be>test)/\l\Utest/i;
print $_;
./demo9-7
This is a Test.
This is a tEST.


  1. 另一个使用正则表达式的是@result = split //,$string,操作符split会根据分隔符(写成模式)将一个字符串截断成子字符串列表。
    demo9-8:
#!/usr/bin/perl
$input = "Zhao:Qian:Sun:LI:Zhou:Wu:Zheng:Wang";
foreach (split /:/, $input){
  print $_."\n";
}

分隔符自己不会出现在列表中

./demo9-8
Zhao
Qian
Sun
LI
Zhou
Wu
Zheng
Wang

  • 如果字符串中两个分隔符连着就会产生空字段,但是Perl会忽略末尾的空字段:
    demo9-9:
#!/usr/bin/perl
$input = "::Zhao::Qian::Sun::LI::Zhou::Wu::Zheng::Wang::";
foreach (split /:/, $input){
  print $_."\n";
}

在原来的每个字段之间和前后都加了额外的分隔符,但是结尾的空字段被舍弃了

./demo9-9


Zhao

Qian

Sun

LI

Zhou

Wu

Zheng

Wang

  • split的默认行为是用空白\s+分割存放于$_内的字符串:
    demo9-10:
#!/usr/bin/perl
$_ = " Zhao Qian Sun LI Zhou Wu Zheng Wang ";
foreach (split){
  print $_."\n";
}

稍有不同之处在于所有空字段都被舍弃(因为连续的空白字符本身就匹配了\s+模式,所以不存在字符串内部的空字段):

./demo9-10
Zhao
Qian
Sun
LI
Zhou
Wu
Zheng
Wang

标准的写法可能是 split /\s+/, $_;
demo9-11:

#!/usr/bin/perl
$_ = " Zhao Qian Sun LI Zhou Wu Zheng Wang ";
foreach (split /\s+/, $_){
  print $_."\n";
}

这样则可以保留字符串前面的空字段,别忘了连续的空白字符本身就匹配了\s+模式,所以不存在字符串内部的空字段

./demo9-11

Zhao
Qian
Sun
LI
Zhou
Wu
Zheng
Wang

  • split操作符功能相反的是join函数(没错,split是操作符,但是join是函数),不过join并没有使用模式:
    $result = join $glue, @pieces;
    join函数的功能是把@pieces中的字符串片段$glue胶水粘合成一个字符串$result, $glue保存着充当胶水的字符串而不是模式
    join可以和split结合实现替换功能,效果类似s///g:
    demo9-12:
#!/usr/bin/perl
$_ = " Zhao Qian Sun LI Zhou Wu Zheng Wang ";
@pieces = split;
print join "&", @pieces;
print "\n";

&替换了空格

./demo9-12
Zhao&Qian&Sun&LI&Zhou&Wu&Zheng&Wang


  1. 列表上下文中使用m//会得到有趣的新特性:如果模式匹配成功,会返回所有捕获变量的列表
    demo9-13:
#!/usr/bin/perl
$_ = "Zhang san   Li si  Wang wu  ";
@pieces = /([A-Z][a-z]+)/g;
foreach (@pieces){
  print $_."\n";
}

捕获了([A-Z][a-z]+)首字母大写其余字母小写的单词:

Zhang
Li
Wang

如果有多个捕获模式就会返回多个捕获串:
demo9-14:

#!/usr/bin/perl
$_ = "Zhang san   Li si  Wang wu  ";
@pieces = /([A-Z][a-z]+)\s+(\w+)/g;
foreach (@pieces){
  print $_."\n";
}

返回的列表里包含了所有捕获串

./demo9-14
Zhang
san
Li
si
Wang
wu

特别地,如果捕获的模式恰好是两个,则可以用哈希结构存储m//返回的结果,这种情况下,第一个捕获的变量是哈希的键,第二个捕获的是哈希的值:
demo9-15:

#!/usr/bin/perl
$input = "Zhang san   Li si  Wang wu  ";
%hash = ($input =~ /([A-Z][a-z]+)\s+(\w+)/g);
foreach (keys %hash){
  print $_."=>".$hash{$_}."\n";
}
./demo9-15
Wang=>wu
Li=>si
Zhang=>san


  1. 正则表达式中的量词:*, +, ?, {M,N}默认都是贪婪量词,即会尽可能多的匹配,总会匹配到最大的字符串,而如果想要使用非贪婪量词,只需要在原来的量词后面加上?*?, +?, ??, {M,N}?,这种情况下会尽可能少的匹配:
    demo9-16:
#!/usr/bin/perl
$_ = "TAG a b c d ef ghi jk TAG lmn opq TAG rst uvw xyz TAG";
if(m/TAG (.+) TAG/){
     print "Greedy:".$&."\n";
}
if(m/TAG (.+?) TAG/){
     print "Non-greedy:".$&."\n";
}
 ./demo9-16
Greedy:TAG a b c d ef ghi jk TAG lmn opq TAG rst uvw xyz TAG
Non-greedy:TAG a b c d ef ghi jk TAG

  1. 使用钻石操作符的新特性,结合正则表达式,使用Perl来修改文件:
    $^I变量中是字符串时,将赋予钻石操作符<>新的特性:首先将@ARGV中字符串对应的文件改名,加上后缀,后缀内容即是$^I内存储的值,修改输入指向这个文件,然后打开一个新文件,命名为原来的文件名,修改输出到这个新文件。
    demo9-17:
#!/usr/bin/perl -w 
use strict;
$^I=".bak";
chomp(my $date = `date`);
while(<>){
    s/Date:.*/Date:$date/;
    print;
}

另外创建一个文件写上“Date:”, 则每次运行程序,都会把Date后的时间更新为当前时间

echo "Date: " > test
./demo9-17 test

这时目录下生成一个test.bak文件,存储着修改前的内容。 while循环里每次都会从test.bak里读取一行放到$_里,然后用s///修改后,打印到test文件里(输出已经被<>重定向到新文件里了)
如果$^I为空,则修改就会直接作用在源文件上


因为这个程序太普遍,所以有了它的简化版本:
perl -p -i.bak -w -e 'chomp(my $date=`date`);' -e 's/Date:.*/Date:$date/' tes*
可以直接在命令行执行,作用和 demo9-17相同
-p选项让perl自动生成一段类似下面这样的小程序:

while(<>){
print;
}

-e后面跟着程序代码,会插入到while循环之中
-i.bak$^I=".bak"的作用相同,如果只有-i那就像是把$^I变量置为空,则不会有备份动作发生。
最后一个参数表示设定@ARGV的值,这里使用了通配符

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容