Perl语言入门笔记 第九章 用正则表达式处理文本

=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 "$_\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; #去除开头和结尾的空白符,但运行可能会慢,由于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$2\E with $1/i; #$_现在成了"I saw FRED with barney"
	
	使用小写形式(\l与\u)时,他们只会影响紧跟其后的第一个字符:
		s/(fred|barney)/\U$1/ig; #$_替换后为"I saw Fred with Barney"
    
    s/(fred|barney)/\u\L$1/ig; # 第一个字母大写,其它字符小写
    也可以用在双引号的字符串中:
    	print "Hello, \L\u$name\E, would you like to play a game?\n"

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 \t     test.\n";
		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\n";
		用等号,默认匹配的是$_
		之前在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";
		#打印: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 \nthan Barney is \nat bowling, \nWilma.\n";
	^代表开头,$代表结尾,m代表匹配多行(m可看做multiple lines),
	如:print "Found 'wilma' at start of line\n" if /^wilma\b/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:.*\n//;
		s/^Date:.*/Date: $date/;
		print;
	}	
	
	my $date = localtime;
	钻石操作符会读取命令行参指定的那些文件,程序的主循环一次会读取,更新及输出一行
		
从命令行直接编辑:
	假设你需要更新上百个文件,把里面拼错成Randall的名字改成只有一个l的Randal。你可以写个和之前类似的程序完成此事,或者在命令行上使用如下单行程序一步完成。
		$perl -p -i.bak -w -e 's/Randall/Randal/g' fred*.dat
			
=cut


posted @ 2015-09-21 01:47  笑面浮屠  阅读(242)  评论(0编辑  收藏  举报