在Perl中使用正则表达式处理文本 $_ = "He's out bowling with Barney tonight."; s/Barney/Fred/; # 将所有Barney替换为Fred print "$_/n";s/文本替换可以在替换的同时引用原字符串接上例: s/with (/w+)/against $1's team/; print "$_/n"; # says "He's out bowling against Fred's team tonight."替换时将with后的单词留在了替换后的字符串中
替换的例子: $_ = "green scaly dinosaur"; s/(/w+) (/w+)/$2, $1/; # "scaly, green dinosaur" 替换了位置 s/^/huge, /; # "huge, scaly, green dinosaur" 使用锚点在开头添加 s/,.*een//; # "huge dinosaur" 删除特定模式字符串,注意删除的是,scaly,green是由于贪婪匹配 s/green/red/; # "huge dinosaur" 失败的匹配,字符串没有任何修改 s//w+$/($`!)$&/; # "huge (huge !)dinosaur" 把结尾的词替换成结尾的词之前的部分加括号和! s//s+(!/W+)/$1 /; # "huge (huge!) dinosaur" 空格后跟!和非字母数字字符的模式去掉了前面的空格,后面加空格 s/huge/gigantic/; # "gigantic (huge!) dinosaur" 把huge替换为gigantic,这里只替换了第一个匹配的字串(非全局)
替换表达式的值在替换成功情况下为真,否则为假 $_ = "fred flintstone"; if (s/fred/wilma/) { print "Successfully replaced fred with wilma!/n"; }
s///替换仅能替换第一个出现的模式使用/g modifier/g means Global Replacements 替换每一次匹配的模式 $_ = "home, sweet home!"; s/home/cave/g; print "$_/n"; # "cave, sweet cave!"
有用的短程序 $_ = "Input data/t may have extra whitespace."; s//s+/ /g; # Now it says "Input data may have extra whitespace."使用s//s+/ /g 将所有连续空白都替换为单个空格字符
s/^/s+//; # 去除行首所有连续空白 s//s+$//; # 去除行末所有连续空白 s/^/s+|/s+$//g; # 去除行首和行末的连续空白
替换也允许自定义边界字符如s#^https://#http://#; #把行首https://替换为http://当成对使用时括号时应使用两对,对应匹配的模式和替换模式 s{fred}{barney}; s[fred](barney); s<fred>#barney#;均为合法的形式
匹配中使用的modifier /i, /x, 和 /s 同样有效, 使用顺序对效果没有影响 s#wilma#Wilma#gi; #把所有WiLmA或WILMA之类的单词替换为Wilma s{_ _END_ _.*}{ }s; #把END标志及其后所有的行都清除掉
=~ 操作符指定替换的变量,在替换中仍然有效 $file_name =~ s#^.*/##s; # 把文件名中的路径信息去除掉
/U 用于将/U后的所有内容强制转换为大写 $_ = "I saw Barney with Fred."; s/(fred|barney)//U$1/gi; # $_ is now "I saw BARNEY with FRED."/L 用于把/L后的所有内容强制转换为小写
这两个操作符默认影响所有在它们之后的内容,使用/E关闭大小写强制效果s/(/w+) with (/w+)//U$2/E with $1/i; # $_ is now "I saw FRED with barney."
小写的/u和/l只影响紧接着它们的一个字符大小写的/U /u等可以混用例如: s/(fred|barney)//u/L$1/ig; # $_ is now "I saw Fred with Barney."无论这两个单词原来的大小写形式为何,都修改为大写首字母的形式
注意这些大小写操作符在print中同样有效 print "Hello, /L/u$name/E, would you like to play a game?/n";对变量$name进行操作,注意不要忘了用/E 关闭开关
split操作符 用于将字符串按照分隔符划分除了使用逗号文件如CSV文件,对逗号分隔最好使用Text::CSV包来处理
@fields = split /separator/, $string;
例子@fields = split /:/, "abc:def:g:h"; # 得到("abc", "def", "g", "h")
如果分隔符出现多次,会出现为值为undef的元素@fields = split /:/, "abc:def::g:h"; # gives ("abc", "def", "", "g", "h")
在开始处出现的undef会被填进结果,而在末尾的不会,要小心!@fields = split /:/, ":::a:b:c:::"; # gives ("", "", "", "a", "b", "c")末尾
没有undef的元素
根据空白字符切分的例子 my $some_input = "This is a /t test./n"; my @args = split //s+/, $some_input; # ("This", "is", "a", "test.")能够处理连续的空格字符
split操作符默认处理$_变量,默认根据空格字符切分 my @fields = split; # like split //s+/, $_;
当依据//s+/切分时,行首的空白字符并不会造成undef元素,小心!如果需要把行首字符处理为undef,使用split ' ',$your_string;
与split相对的join操作注意join的第一个参数是一个string不是正则表达式!!!my $result = join $glue, @pieces;
例子 my $x = join ":", 4, 6, 8, 10, 12; # $x is "4:6:8:10:12"
连接符紧接在数组元素后面,当元素不足两个时,一个连接符都不会出现 my $y = join "foo", "bar"; # $y的值"bar",没有连接符foo my @empty; # empty array my $empty = join "baz", @empty; # $empty的值为空undef
可以综合使用split和join修改分隔符,如 $x = "4:6:8:10:12" my @values = split /:/, $x; # @values is (4, 6, 8, 10, 12) my $z = join "-", @values; # $z is "4-6-8-10-12"
list语境下的match匹配当match正则表达式用在数组语境下时匹配成功,返回值是对应的match memeory, 匹配不成功, 则是一个空数组
$_ = "Hello there, neighbor!"; my($first, $second, $third) = /(/S+) (/S+), (/S+)/; print "$second is my $third/n";
/g modifier和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 Slatetext每次获得一个单词,然后放入words数组, 这段代码的功能与split相似,不同在于使
用正则表达式匹配确定内容而非分隔符
当存在多于一个括号时,匹配结果返回多个string例子: my $data = "Barney Rubble Fred Flintstone Wilma Flintstone"; my %last_name = ($data =~ /(/w+)/s+(/w+)/g);通过使用多个()括号,last_name
哈希表被初始化为{Barney => Rubble,Fred => Flintstone,Wilma => Flintstone,}
贪婪的数量修饰Greedy Quantifiers+ * {0,5} 等数量修饰符匹配方式都是贪婪的(尽可能匹配最长)例子: 用/fred.+barney/ 模式 去匹配fred and barney went bowling last night.正则表达式引擎的工作方式(由于优化的存在,实际过程可能不同)1. 首先匹配到了fred2. .+贪婪匹配,一直匹配到了night.3. barney发现已经在词尾了,无法匹配,.+匹配的位置向前移动寻找可能的匹配,直到成功或失败这样的匹配方式涉及大量的回退.性能会受影响
非贪婪数量修饰, 在后面加?+? 匹配一次或多次(非贪婪,匹配仅可能短的字符串)使用贪婪和非贪婪数量修饰,在测试匹配结果上,没有任何不同
当预期.+分隔的两个模式间距离很近的时候,使用非贪婪模式有可能能够提高正则表达式引擎解析的效率.
但是使用memory时可能会造成非常大的不同!例如:使用正则模式替换去除<BOLD>标记s#<BOLD>(.*)</BOLD>#$1#g;对I'm talking about the cartoon with Fred and <BOLD>Wilma</BOLD>! 能够正确作
用.对I thought you said Fred and <BOLD>Velma</BOLD>, not <BOLD>Wilma</BOLD>s#<BOLD>(.*)</BOLD>#$1#g处理的结果是匹配最长的<BOLD>和</BOLD>留下了I thought you said Fred and Velma</BOLD>, not <BOLD>Wilma不是预期的结果改为s#<BOLD>(.*?)</BOLD>#$1#g;就能够正确匹配替换掉最接近的<BOLD>和</BOLD>标记对
非贪婪模式的数量修饰符还有:{5,10}?{8,}??? 不太直观,匹配一次或多次,尽可能不匹配^_^对于{3}这样指定重复次数的,加不加非贪婪符没有任何不同,最好不要使用{3}?,以免降低可读性
多行匹配
经典正则表达式匹配的只是单行字符串Perl中m//能够匹配到多行的字符串
$_ = "I'm much better/nthan Barney is/nat bowling,/nWilma./n"; print "Found 'wilma' at start of line/n" if /^wilma/b/im;锚点^ $作用于整个字串的开头和结尾/m选项打开这个选项使得^和$两个锚点作用于字串中每一行的开头和结尾,而不是整个字串的开头和结尾
在s///替换中也可以使用/m选项 open FILE, $filename or die "Can't open '$filename': $!"; my $lines = join '', <FILE>; #逐行读入文件内容,将整个文件变成一个
字符串, #注意join的第一个参数是个空字符,这个文件不能太大 $lines =~ s/^/$filename: /gm;打开一个文件,并将文件名加入文件的每一行开始处.这种做法非常没有效率,不能作为处理文件的常规方法!
较为复杂(也更实用)的例子:处理多个文件,修改文件中的每一行 #!/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; }
my $date = `date`; 获取系统当前日期另一种方法是my $date = localtime; 打印出来的结果是:The current date is: 02/01/2007 ThuEnter the new date: (mm-dd-yy)Thu Feb 1 14:13:21 2007
特殊变量$^I当特殊变量$^I被设置成某个string时,这个string会被用来作为备份文件的扩展名在上例中,假如我们传递了参数fred03.dat给perl程序Perl首先打开文件fred03.dat,将其改名为fred03.dat.bak, 而后<>操作符创建一个新
文件名字叫fred03.dat,并把fred03.dat这个新文件作为默认输出,所有print都进入了
fred03.dat这个新文件.创建新文件时,新文件的owner等属性都和源文件相同
$^I设置为~ 的方式,与 emacs backup的方式兼容$^I也可以被设置为空字符串,这样就没有backup file
$^I最好不要被设置为空,这样你的正则表达式没有按预期工作时,仍然有干净的原文件可用.
Perl允许使用更简单的方式,在一行命令行中实现上述的功能:$ perl -p -i.bak -w -e 's/Randall/Randal/g' fred*.dat
各参数解释意义如下:
-p参数, 生成循环读取每行的框架 while (<>) { print; }如果使用-n参数,则生成的程序没有while循环,仅有print语句
-i.bak相当于$^I=".bak" 设置$^I变量
-w 打开所有warning
-e 选项后面的内容是Perlcode,使用-p时,这些语句被放入循环内,print前可以使用-e输入多行perl代码, 注意每个-e后面的语句都必需要写分号,只有最后一个
分号可以被省略.
这行命令被解释为: #!/usr/bin/perl -w $^I = ".bak"; while (<>) { s/Randall/Randal/g; print; }输入参数为fred*.dat @ARGV保持了所有符合这个通配符的文件名
将所有fred*.dat文件中的Randall修改成Randal