阅读笔记-精通正则表达式-第2章-入门示例及扩展-1
1. 温度转化的示例
这一章的开始就介绍了Perl,并且使用Perl中的正则表达式,不断的加强一个温度转化的程序的功能。
这里直接上完整的示例了:
$input = <STDIN>; # 接收用户输入的一行文本
chomp($input); # 去掉$input尾部的换行符
if( $input =~ m/^\s*([-+]?[0-9]+(\.[0-9]+)?)\s*([C,F])\s*$/i ) {
# $1保存了整体数值,$2保存了小数部分,S3保存了C或者F
$inputNum = $1;
$type = $3;
if($type =~ m/c/i) { # 如果是C或者c
$celsius = $inputNum;
$fahrenheit = ($celsius * 9 / 5) + 32;
} else { # 如果不是C也不是c
$fahrenheit = $inputNum;
$celsius = ($fahrenheit - 32) * 5 / 9;
}
printf "%.2f C is %.2f F\n", $celsius, $fahrenheit;
} else { #匹配失败了
print "Expecting a number followed by \"C\" or \"F\",\n";
print "so I don't understand \"$input\".\n";
}
重点解释一下这行代码:$input =~ m/^\s*([-+]?[0-9]+(\.[0-9]+)?)\s*([C,F])\s*$/i
$input是一个变量
=~ 表示匹配的意思
m/ ... / 表示内部是一个正则表达式
m/ ... /i表示匹配的正则表达式不区分大小写
\s表示空白字符,包括空格,制表符,换行符,回车符。
配的逻辑:行起始,任意多个空白字符,零个或者一个[-+],一个或者多个数字,小数部分,任意多个空白字符,一个C或者一个F,任意多个空白字符行结束。
这个例子中,比较重要的就是,匹配一个字符串,并且能够记录其中匹配的部分,即$1,$2,$3记录正则表达式中的三个括号内容匹配到的信息。实际中,只是用了 $1,$3,$2没有用到,可是使用非捕获型括号,(?:···)。这样的话,$2就能够保存以前$3中的信息了。
2. Perl安装试用
此前没用过PERL,虽然上面的代码是书中给出的,总有实际用Perl跑一下,因此下面简单说说我下载并且使用PERL的经历。
首先,找到PERL的官方网站:http://www.perl.org/。
然后,下载对应操作系统的PERL二进制安装包:http://strawberryperl.com/,下载的是5.12.3.0这个版本,40MB的文件,strawberry-perl-5.12.3.0.msi。
接着,双击strawberry-perl-5.12.3.0.msi这个文件。注意:安装的目录不能有空格,默认安装目录为"C\strawberry\"
接着,在桌面上新建一个文件,命名为HelloWorld.pl,使用记事本方式打开这个文件,输入一下代码,并保存文件。
$input = <STDIN>;
接着,双击桌面上这个文件,得到如下运行结果:
最后,在窗口中,回车,该窗口就会退出了。$input = <STDIN>;就是接收一行字符,否则程序一闪就过去了,看不清输出内容。
总体来说,如果用两个字来形容PERL,就是简单,用一个字来形容就是爽。没有IDE的启动,没有编译过程,没有各种警告与错误的提示。双击文件就运行。
最后,将温度转化的代码使用PERL实际运行了一下,保证代码确实能够正常运行。
3. 知识总结
·Perl用$variable =~ m/regex/来判断一个正则表达式regex是否匹配某个字符串variable,如果匹配成功,返回true,否则返回false。
·相关减记法
\t 制表符
\n 换行符
\r 回车符
\s 任何空白字符(包括:空格符,制表符,回车符,换行符)
\S 除\s之外的任何字符
\w [a-zA-Z0-9]
\W 除\w之外的任何字符,也就是[^a-zA-Z0-9]
\d [0-9],即数字
\D 除\d外的任何字符,即[^0-9]
·/i 表示此次匹配不区分大小写
·(?:···) 这个麻烦的写法可以用来分组文本,但并不捕获
·匹配成功之后,Perl可以用$1,$2,$3之类的变量来保存相对应的(···)括号内的子表达式匹配的文本。使用这些变量,我们能够用正则表达式从字符串中提取信息。
4. 使用正则表达式修改文本
匹配字符串的格式:variable =~ m/regex/
修改字符串的格式:variable =~ s/regex/replacement/
几个使用regex修改文本的例子:
4.1 公函生成程序
假设有一个公函系统,它包含很多公函模板,其中有一些标记,对每一封具体的公函来说,标记部分的值可以定制,这个定制的就是通过使用正则来修改标记部分来实现的。
模板:
Dear =FIRST=,
You have been chosen to win a brand new =TRINKET=! Free!
Could you use another =TRINKET= in the =FAMILY= household?
Yes =SUCKER=, I bet you could! Just respond by......
定制:
$given = "Tom";
$family = "Cruise";
$wunderprize = "100% genuine faux diamond";
填写模板
$letter =~ s/=FIRST=/$given/g;
$letter =~ s/=FAMILY=/$famliy/g;
$letter =~ s/=SUCKER=/$given $family/g;
$letter =~ s/=TRINKET=/fabulous $wunderprize/g;
这里的/g是“全局替换”的修饰符。它告诉s/···/···/在第一次替换完成之后继续搜索更多的匹配文本,进行更多的替换。
4.2 修正股票价格
对于"9.0500000037272",希望得到的形式可能是"9.05"。假设:要求是保留小数点后两位数字,如果第三位不为零,也要保留。
$price =~ s/(小数点 小数部分的前两位 不为零的小数部分的第三位) 小数部分分的其他位/$1/
$price =~ s/(\.\d\d[1-9]?)\d*/$1/
注意:此处直接匹配的是小数点开始的部分,小数点起到了开始点的作用。没有直接去匹配整体的数字,如果整体匹配的话,反而会变得更加麻烦。这个例子的问题本身就是针对可能存在的小数部分进行修正,因此直接匹配小数部分更加合理。
4.3 处理邮件的小工具
要求编写一个名为mkreply的程序,从king.in中的邮件信息文件中,解析出邮件头部的一些字段:日期,主题等。把解析出的字段保存到king.out上面。
输入文本,king.in:
Received: from elvis@localhost by tabloid.org (8.11.3) id HA8CMY
Received: from tabloid.org by gateway.net (8.12.5/2) id N8XBK
To: jfriedl@regex.info (Jeffrey Friedl)
From: Elvis@tabloid.org (The King)
Date: Thu, Feb 29 2007 11:15
Message-Id: <2007022939939.KA8CMY@tabloid.org>
Subject: Be seein' ya around
Reply-To: elvis@hh.tabloid.org
X-Mailer: Madam Zelda's Psychic Orb [version 3.7 PL92]
Sorry I haven't been around lately. A few years back I checked
into that ole heartbreak hotel in the sky, ifyaknowwhatImean.
The Duke says "hi".
Elvis
输出文本,King.out:
From: jfriedl@regex.info (Jeffrey Friedl)
Subject: Re: Be seein' ya around
On Thu, Feb 29 2007 11:15 The King wrote:
|> Sorry I haven't been around lately. A few years back I checked
|> into that ole heartbreak hotel in the sky, ifyaknowwhatImean.
|> The Duke says "hi".
|> Elvis
perl的命令行格式:%perl -w mkreply king.in > king.out
其中,-w用来打开Perl的额外警告功能。
mkreply的完整代码
open(OutFile, ">$ARGV[1]") or die "open out file fail";
while($line = <InFile>) {
if($line =~ m/^\s*$/) { # 如果存在空行
last; # 就立即结束while循环
}
if($line =~ m/^Subject: (.*)/i) { # "Subject: Be seein' ya around"
$subject = $1;
}
if($line =~ m/^Date: (.*)/i) { # "Date: Thu, Feb 29 2007 11:15"
$date = $1;
}
if($line =~ m/^Reply-To: (\S+)/i) { # "Reply-To: elvis@hh.tabloid.org"
$reply_address = $1;
}
if($line =~ m/^From: (\S+) \(([^()]*)\)/i) { # "From: Elvis@tabloid.org (The King)"
$reply_address = $1;
$from_name = $2;
}
}
if( not defined($reply_address) ) {
die "couldn't glean the required information! reply_address.";
}
if( not defined($from_name) ) {
die "couldn't glean the required information! from_name.";
}
if( not defined($subject) ) {
die "couldn't glean the required information! subject.";
}
if( not defined($date) ) {
die "couldn't glean the required information! date.";
}
print OutFile ("To: $reply_address ($from_name)\n");
print OutFile ("From: jfriedl\@regex.info (Jeffrey Friedl)\n");
print OutFile ("Subject: Re: $subject\n");
print OutFile ("\n");
print OutFile ("On $date $from_name wrote:\n");
while($line = <InFile>) {
print OutFile ("|> $line");
}
close(InFile);
close(OutFile);
说明几点:
·书上的程序代码我没有调通,读文件还行,写文件好像有问题,直接修改了读写文件的方式。
命令行格式为:%perl -w mkreply.pl king.in king.out
Perl的命令行格式为 %perl
·书中的程序中,有两处关于\s的使用错误了。
($line =~ m/^Reply-To: (\S+)/i)
($line =~ m/^From: (\S+) \(([^()]*)\)/i)
这两行中的(\S+)是匹配连续的非空白字符,书中误写为了(\s+)。