=pod #第九章:用正则表达式处理文本 用s///进行替换 s///查找替换功能 s/Barney/Fred/; #把Barney替换成Fred s/with (w+)/against $1's team/; 例子: $_ = "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" 用/g进行全局替换 s///只会一次替换,默认的行为 $_ = "home, sweet home!"; s/home/cave/g; print "$_ "; #打印"cave, sweet cave" 一个常见的全局替换是缩减空白,也就是将任何连续的空白转换成单一空格: $_ = "Input data may have extra whitespace"; s/s+/ /g; #现在它变成了"Input data may have extra whitespace." s/^s+//; #将开头的空白替换成空字符串 s/s+$//; #将结尾的空白替换成空字符串 s/^s+|s+$//g; #去除开头和结尾的空白符,但运行可能会慢,由于perl的引擎问题 不同的定界符: 替换时,也可以像m//和qw//一样,我们也可以改变s///的定界符,但由于替换符会用到三个定界符,所以情况又有点不同。 s#^https://#http://# s{fred}{barney}; s[fred](barney); s<fred>#barney#; 可用替换修饰符: s#wilma#Wilma#gi; #将所有的WilmA或者WILMA一律替换为Wilma s{__END__.*}; #将__END__标记和它后面的所有内容都删掉 绑定操作符: $file_name =~ s#^.*/##s; #将$file_name中所有的Unix风格的路径全部去除 无损替换: 如果需要同时保留原始字符串和替换后的字符串,该怎么办?传统的做法是先复制一份拷贝后再替换: my $original = 'Fred ate 1 rib'; my $copy = $original; $copy =~ s/d+ ribs?/10 ribs/; 替换成一步: (my $copy = $original) =~ s/d+ ribs?/10 ribs/; 新写法: use 5.014; my $copy = $original =~ s/d+ ribs?/10 ribs/r; #先做替换,再复制 大小写切换: U: 使目标变为大写 $_ = "I saw Barney with Fred."; s/(fred|barney)/U$1/gi; #$_现在成了"I saw barney with fred." L: 使目标变为小写 s/(fred|barney)/L$1/gi; #$_现在成了"I saw barney with fred" E: 关闭大小写转换,不然会影响后面的字符 s/(w+) with (w+)/U$2E with $1/i; #$_现在成了"I saw FRED with barney" 使用小写形式(l与u)时,他们只会影响紧跟其后的第一个字符: s/(fred|barney)/U$1/ig; #$_替换后为"I saw Fred with Barney" s/(fred|barney)/uL$1/ig; # 第一个字母大写,其它字符小写 也可以用在双引号的字符串中: print "Hello, Lu$nameE, would you like to play a game? " split操作符: 根据给定的模式拆分字符串,对于使用制表符、冒号、空白或任意符号分割不同字段数据的字符串来说,这个操作符分解提取字段相当方便,只要你能将分隔符写成模式,就可以使用split分解数据,它的使用用法如下: my @fields = split /separator/, $string; my @fileds = split/:/, "abc:def:g:h"; #得到("abc", "def". "g", "h") 如果两个分隔符连在一起,就会产生空字段: my @fields = split/:/, "abc:def:g:h"; #得到("abc", "def", "", "g", "h") 以CPAN上的Text::CSV库处理csv文件最好。 split会保留开头处的空字段,却会舍弃结尾处的空字段。 my @field = split /:/, ":::a:b:c:::"; #得到("", "", "", "a", "b", "c") 根据split的/s+/模式根据空白符分隔字段也是比较常见的做法,该模式把所有连续空开都视作单个空格并以此切分数据: my $some_input = "This is a test. "; my @args = split /s+/, $some_input; #得到("This", "is", "a", "test.") 默认split会以空白符分隔$_中的字符串: my @fields = split; #等效于split /s+/, $_; join函数: join函数和split函数恰好相反,他会将这些片段接合成一个字符串。join函数的用法如下: my $result = join /$glue/, @pieces; my $x = join ":", 4, 6, 8, 10, 12; # $x为"4:6:8:10:12" 列表上下文中的m//: 在列表上下文中使用模式匹配操作符(m//)时,如果模式匹配成功,那么返回的是所有捕获变量的列表;如果匹配失败,则会返回空列表: $_ = "Hello there, neighbor!"; my($first, $second, $third) = /(S+) (S+) (S+)/; print "$second is my $third "; 用等号,默认匹配的是$_ 之前在s///的例子中的/g修饰符同样可以用在m//操作符上,其效果就是让模式能够匹配到字符串中的多个地方。 如:my $text = "Fred dropped a 5 ton granite block on Mr.Slate"; my @words = ($text =~ /([a-z]+)/ig); print "Result: @words "; #打印:fred dropped a ton granite block on Mr. Slate 这好比反过来用split:正则模式制定的并非想要去除的部分,反而是要留下的部分。 一个字符串变成hash my $data = "Barney Rubble Fred Flintstone Wilma Fllintstone"; my %last_name = (data =~ /(W+)s+(w+)/g); 更强大的正则表达式: 非贪婪量词: /fred.+barney/匹配fred, and braney bowling 因为.+是贪婪的会匹配更多的单词,会认为barney单词然后跳过了 /fred.+?barney/匹配fred and braney 回溯动作,逐个匹配需要的内容 CPAN上的模块可以处理HTML或类似的标记语言,使用模块为HTML::Parser 贪婪样式: s#<BOLD>(.*)</BOLD>#$1#g; 非贪婪样式: s#<BOLD>(.*?)</BOLD>#$1#g; 非贪婪样式是在通配符后面加上?,如+?,*?,{5,10}?或者{8, }?, ?? 跨行的模式匹配: 传统的正则表达式是处理单行文本,perl可以处理任意长度的字符串,其模式匹配自然也可以处理多行文本 下面的写法可以表示4行的文本: $_ = "I'm much better than Barney is at bowling, Wilma. "; ^代表开头,$代表结尾,m代表匹配多行(m可看做multiple lines), 如:print "Found 'wilma' at start of line " if /^wilma/im; open FILE, $filename or die "Can't open '$filename':$|"; my $lines = join '', <FILE>; $lines =~ s/^/$filename: /gm; ps:将整个文件读进一个变量,然后把文件名作为每一行的前缀进行替换。 一次更新多个文件: 下面的程序有问题,待解决:$^I好像并不是这样的用法 #!/usr/bin/perl -w use strict; chomp(my $date = `date`); #用了bash shell里的date指令 $^I = ".bak"; #能循环找当前文件夹下的文件???? while(<>) { s/^Author:.*/Author: Randal L. Schwartz/; s/^phone:.* //; s/^Date:.*/Date: $date/; print; } my $date = localtime; 钻石操作符会读取命令行参指定的那些文件,程序的主循环一次会读取,更新及输出一行 从命令行直接编辑: 假设你需要更新上百个文件,把里面拼错成Randall的名字改成只有一个l的Randal。你可以写个和之前类似的程序完成此事,或者在命令行上使用如下单行程序一步完成。 $perl -p -i.bak -w -e 's/Randall/Randal/g' fred*.dat =cut