4.3 perl--正则表达式处理文本

如果m//模式匹配现象成文字处理器的“查找”功能,那么s///替换操作符就是“查找并替换”功能。

$_="green scaly dinosaur";
s/(\w+)(\w+)/$2,$1/g;  #替换后为“scaly,green dinosaur”

上面的例子中/g为全局替换。我们在缩减空白的时候,可以这么做:

s/^s+// ;  #将开头的空白替换成空字符串
s/s+$//;   #将结尾的空白替换为空字符串

也可以这么写:

s/^\s+|\s+$//g; 去除开头和结尾的空白符

s///也可以采用不同的定界符,但是如果是有左右之分的话,就必须成对使用。比如s{fred}{barney} 同时我们也可以使用之前提到的修饰符,顺序无限后,比如/i,/x,/s

下面是正则表达式中的一些常用模式。

/pattern/ 结果

.	匹配除换行符以外的所有字符
x?	匹配 0 次或一次 x 字符串
x*	匹配 0 次或多次 x 字符串,但匹配可能的最少次数
x+	匹配 1 次或多次 x 字符串,但匹配可能的最少次数
.*	匹配 0 次或一次的任何字符
.+	匹配 1 次或多次的任何字符
{m}	匹配刚好是 m 个 的指定字符串
{m,n}	匹配在 m个 以上 n个 以下的指定字符串
{m,}	匹配 m个 以上 的指定字符串
[]	匹配符合 [] 内的字符
[^]	匹配不符合 [] 内的字符
[0-9]	匹配所有数字字符
[a-z]	匹配所有小写字母字符
[^0-9]	匹配所有非数字字符
[^a-z]	匹配所有非小写字母字符
^	匹配字符开头的字符
$	匹配字符结尾的字符
\d	匹配一个数字的字符,和 [0-9] 语法一样
\d+	匹配多个数字字符串,和 [0-9]+ 语法一样
\D	非数字,其他同 \d
\D+	非数字,其他同 \d+
\w	英文字母或数字的字符串,和 [a-zA-Z0-9] 语法一样
\w+	和 [a-zA-Z0-9]+ 语法一样
\W	非英文字母或数字的字符串,和 [^a-zA-Z0-9] 语法一样
\W+	和 [^a-zA-Z0-9]+ 语法一样
\s	空格,和 [\n\t\r\f] 语法一样
\s+	和 [\n\t\r\f]+ 一样
\S	非空格,和 [^\n\t\r\f] 语法一样
\S+	和 [^\n\t\r\f]+ 语法一样
\b	匹配以英文字母,数字为边界的字符串
\B	匹配不以英文字母,数值为边界的字符串
a|b|c	匹配符合a字符 或是b字符 或是c字符的字符串
abc	匹配含有 abc 的字符串
(pattern)	() 这个符号会记住所找寻到的字符串,是一个很实用的语法。第一个 () 内所找到的字符串变成 $1 这个变量或是 \1 变量,第二个 () 内所找到的字符串变成 $2 这个变量或是 \2 变量,以此类推下去。
/pattern/i	i 这个参数表示忽略英文大小写,也就是在匹配字符串的时候,不考虑英文的大小写问题。
\	如果要在 pattern 模式中找寻一个特殊字符,如 "*",则要在这个字符前加上 \ 符号,这样才会让特殊字符失效

下面给出一些例子:

范例 说明

/perl/	找到含有 perl 的字符串
/^perl/	找到开头是 perl 的字符串
/perl$/	找到结尾是 perl 的字符串
/c|g|i/	找到含有 c 或 g 或 i 的字符串
/cg{2,4}i/	找到 c 后面跟着 2个到 4个 g ,再跟着 i 的字符串
/cg{2,}i/	找到 c 后面跟着 2个以上 g ,再跟着 i 的字符串
/cg{2}i/	找到 c 后面跟着 2个 g,再跟着 i 的字符串
/cg*i/	找到 c 后面跟着 0个或多个 g ,再跟着 i 的字符串,如同/cg{0,1}i/
/cg+i/	找到 c 后面跟着一个以上 g,再跟着 i 的字符串,如同/cg{1,}i/
/cg?i/	找到 c 后面跟着 0个或是 1个 g ,再跟着 i 的字符串,如同/cg{0,1}i/
/c.i/	找到 c 后面跟着一个任意字符,再跟着 i 的字符串
/c..i/	找到 c 后面跟着二个任意字符,再跟着 i 的字符串
/[cgi]/	找到符合有这三个字符任意一个的字符串
/[^cgi]/	找到没有这三个字符中任意一个的字符串
/\d/	找寻符合数字的字符,可以使用/\d+/来表示一个或是多个数字组成的字符串
/\D/	找寻符合不是数字的字符,可以使用/\D+/来表示一个或是更多个非数字组成的字符串
/\*/	找寻符合 * 这个字符,因为 * 在常规表达式中有它的特殊意思,所以要在这个特殊符号前加上 \ 符号,这样才会让这个特殊字符失效
/abc/i	找寻符合 abc 的字符串而且不考虑这些字符串的大小写

无损替换

(my $copy =$original)=^s/\d+ribs?/10 ribs/;

也可以在修饰符r的作用下这么使用

use 5014;
my $copy =$original=^s/\d+ribs?/10 ribs/r;

大小写的转换:

\U  将其后的所有字符转换成大写
\L  将后面的所有字符转换成小写
\E  关闭大小写的功能
\l  \u  小写的形式仅仅影响后面跟的第一个字符
\u\L  表示首字母大写,后面的字符全部小写,顺序无先后

例子: s/(\w+) with (\w+)/\U$2\E with $1/i; 这个例子中仅仅对$2大写,其后的不变。

split操作符

根据模式来拆分字符串;

my @fields =split /separator/,$string;

期间只要模式在某处匹配成功,该处就是当前字段的结尾,下一个字段的开头。

my @fields =split /:/,"abc:def:g:h";  #得到的是("abc","def","g","h")

如果是两个分隔符在一起,就会产生空字段,保留开头处的空字段,舍弃结尾处的空字段。

my @fields =split   #等效于my @fields =split /\s+/,$_;

join函数

它的功能和split相反,它可以把这些片段连接成一个字符串

my $result= join $glue,@pieces;

例如

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

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

my $data ="Barney Rubble Fred Flintstone Wilma Flintstone"
my @words=($data=~/(\w+)\s+(\w+)/g);
my $words=($data=~/(\w+)\s+(\w+)/g);

你看 ,这样就可以将标量变成数组或哈希了。

I thought you said Fred and Velma,notWilma

可以这样来 s#(.*?)#$1#g; 如果不加问号的话,就只有一个结果,加了以后才能有两个

把整个文件读进一个变量,然后把文件名作为每一行的前缀。

open FILE,$filename
   or die "Can't open '$filename':$!";
 my $lines=join '',;
$LINES=~s/^/$filename:/gm;

从命令行直接编辑

在终端中输入 :

$perl -p -i.bak -w -e 's/RANDALL/RANDAL/g' fred*.dat

这个就相当于:

#! /usr/bin/perl -w
$^I=".bak";
while(<>){
 s/RANDALL/RANDAL/g;
 print;
}

-p让perl自动生成一段小程序, -i相当于$^I设为.bak,如果不想备份文件的话,可以直接写-i;-w警告功能;-e后面跟着的是可执行的程序代码

例1:匹配3个$what

#! /usr/bin/perl
use warnings;
use strict;
my $what='fred|barney';
while (<>){
    chomp;
     if (/($what){3}/){
     print "$_\n";
      } 
     }

例2:将Fred换成Larry,同时输出文件名为*.out

#! /usr/bin/perl
use warnings;
use strict;
while(<>){
chomp;
s/Fred/Larry/ig;
print "$_\n";
}

根据上面的程序即可,同时在使用的时候 : perl 程序名 <文件名> 生成的文件名.out 当然也可以指定输出,但是我觉得太麻烦了,就算了吧

例3:将Fred和Barney互换

#! /usr/bin/perl
use warnings;
use strict;
while(<>){
chomp;
s/Fred/Larry/ig;
s/barney/Fred/ig;
s/Larry/Barney/ig;
print "$_\n";
}

例4:在开始前加文本申明:

#! /usr/bin/perl
use warnings;
use strict;
$^I=".bak";
while(<>){
s/\A\#/## Copyright (C) 2013 by Sam \n\#/;
print;
 }

参考资料:

ps:

  1. 上面的方法尽管参考,方法肯定不止一个
  2. 如果跟我一样是菜鸟,请结合我的第之前的博客看如何运行这样的脚本。
药企,独角兽,苏州。团队长期招人,感兴趣的都可以发邮件聊聊:tiehan@sina.cn
个人公众号,比较懒,很少更新,可以在上面提问题,如果回复不及时,可发邮件给我: tiehan@sina.cn