括号配对检测python123_精通正则表达式:第二章(1)

精通正则表达式:第二章

本文关注的是正则表达式,只是因为Perl对正则表达式的支持优于其他语言,所以选用Perl,请不要过多的关心Perl是怎么回事,必要的前置知识会在这里提及。下面开始我们的正则之旅。在本文,会使用·来代替正则表达式中出现的空格。

一、简单易懂的Perl魔法

下面是一段简单的Perl示例程序,功能是将华氏温度转换为摄氏温度。

$celsius = 30;

$fahrenheit = ($celsius * 9 / 5) + 32; #计算华氏温度

print "$celsius C is $fahrenheit F.\n" #输出两种温度

其结果为:

30 C is 86 F.

从这段程序中,我们会发现Perl的几个特点:

普通的变量(如$celsius)以$开头,可以保存数值或者字符串。

\#代表着注释的开始

变量可以出现在引号包围的字符串中,最后会被其实际值替代。口怕!

同时,Perl也提供了流程控制语句,如while:

$celsius = 20;

while ($celsius <= 45)

{

$fahrenheit = ($celsius * 9 / 5) + 32;

print "$celsius C is $ fahrenheit F.\n";

$celsius = $celsius + 5;

}

运行结果如下:

20 C is 68 F.

25 C is 77 F.

30 C is 86 F.

35 C is 95 F.

40 C is 104 F.

45 C is 113 F.

当条件为真的时候,while循环控制的部分就会重复执行,直到条件为假。如果在终端中运行,则就像下面这样:

/~> perl -w CelToFah.pl

这里的-w参数不是必须的,但是加上参数以后,Perl会在可疑的地方报错。这算是一种良好的习惯罢了。由于Perl不是本文的重点,所以介绍就到这里为止,下面是Perl中正则表达式的使用。

二、匹配文本

在Perl中,最简单的正则表达式使用方法就是:检查变量中的文本是否能匹配指定正则表达式。实例片段如下:

if ($reply =~ m/^[0-9]+$/){

print "only digits\n";

} else {

print "not only digits\n";

}

如你所见,第一行的表达式颇有魔法风范:正则表达式是^[0-9]+$;m/.../则通知Perl要对正则表达式进行什么操作,m意味着尝试进行正则表达式匹配,而斜杠则用来标记界限;=~则用来连接对象字符串和正则表达式。

需要注意的是,=~、==、=三者请勿混淆。=~用于正则表达式,=用于变量赋值, 而==则用于测试数值是否相等。字符串是否相等,使用的是eq。在这里,表达式

$reply =~ m/^[0-9]+$/

的返回值取决于变量reply。如果其内容能匹配正则表达式m/^[0-9]+$/,则会返回真。而两端的^$则保证其只包含数字。接下来则是两个例子的结合。

首先,会提示用户输入一个值,接受这个输入并用正则表达式去验证:如果输入的是数值,则计算相应的华氏温度;否则报错。实例如下:

print "Enter a temperature in Celsius:\n";

$celsius = ; #从用户处接受一个输入

chomp($celsius); #去掉换行符

if ( $celsius =~ m/^[0-9]+$/) {

$fahrenheit = ($celsius * 9 / 5) + 32; #计算华氏温度

print "$celsius C is $fahrenheit F\n";

} else {

print "Expecting a number, so I don't understand \"$celsius\".\n";

}

字符里面的转义就不再赘述了。要注意的是,Perl中,字符串和正则表达式的区别既不明显,也不重要,这是它和其他语言的一大区别。运行结果如下:

Enter a temperature in Celsius:

123

123 C is 253.4 F

该版本的Perl浮点数处理的很好……那我就不黑了。

更进一步

我们可以拓展这个例子,使它支持小数和负数。计算部分就交给Perl吧。负数就是一个可选的负号,而小数则是可选的小数点和任意数字。所以拓展后的正则表达式是这样的:

m/^-?[0-9]+(\.[0-9]*)?$/

现在,他就可以匹配-19、0.343这类的数字了,但是.9834这种数字还是无法匹配,由于不是什么大问题,我们会留到很后面再来处理。

成功匹配的副作用

现在,我们除了要匹配数字,还要用户可以输入C和F来标识输入的温度类型,并进行转换。

我们知道,正则表达式可以捕获匹配文本,并在能够在正则表达式之外进行引用。而Perl则通过临时变量$1/$2/$3指向分组内的子表达式匹配的文本。

总之,匹配过程中,使用/1来匹配的文本;而在匹配过后,用$1指向匹配的文本。为此,我们需要修改表达式。首先,忽略并去掉小数部分的匹配,以突出新特点。

m/^([-+]?[0-9]+)([CF])$/

在这个表达式中,使用括号围住了“有价值”的部分,捕捉过后,我们可以决定要使用它们来做什么。现在,我们打算实现之前提到的事情:匹配数字,还要用户可以输入C和F来标识输入的温度类型,并进行转换。

print "Enter a temperature in Celsius:\n";

$input = ; #从用户处接受一个输入

chomp($input); #去掉换行符

if ( $input =~ m/^([-+]?[0-9]+)([CF])$/) {

#程序运行到这里就已经匹配好了。$1保存数字,$2保存符号。

$InputNum = $1;

$type = $2;

if ($type eq "C") {

#输入为摄氏温度,计算华氏温度

$celsius = $InputNum;

$fahrenheit = ($celsius * 9 / 5) + 32;

} else {

#否则,应该是"F",那就计算摄氏温度。

$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";

}

结果如下:

PS E:\LearnPerl> perl -w .\REdigits1.pl

Enter a temperature in Celsius:

22F

-5.56 C is 22.00 F.

PS E:\LearnPerl> perl -w .\REdigits1.pl

Enter a temperature in Celsius:

39C

39.00 C is 102.20 F.

PS E:\LearnPerl> perl -w .\REdigits1.pl

Enter a temperature in Celsius:

oops

Expecting a number followed by "C" or "F",

So I don't understand "oops".

但这里离成功还有一定距离,比如:

无法接受浮点数

不能容许小写的c和f

不能接受数字和字母之间的空格

为了赶上这些距离,我们还有几件事情要做。首先,我们向正则表达式添加小数部分的匹配。修改如下:

m/^([-+]?[0-9]+(\.[0-9]*)?)([CF])$/

这里,我在小数部分添加了一个括号。括号本身虽然没有被我们使用,但是确实影响了引用捕获文本的变量。现在,结果变成了下面这样:

Enter a temperature in Celsius:

11.2F

type is .2

InputNum is 11.2

可以明显的看到,$1匹配的整个数字,也就是外围的第一个括号分组([-+]?[0-9]+(\.[0-9]*)?);而$2则匹配第一个括号分组嵌套的(\.[0-9]*);$3则是原来的变量$2。这样就可以明白,分组的序号由分组的开括号(在 表达式中的顺序有关(从左到右)。

我们现在可以将$type变量的赋值改为$3,或者,使用非捕获型括号。

可以使用(?:...)来表示只分组,不捕获。这样,一是不会影响捕获计数,二是可以提高匹配效率,三是让代码更加清晰。但是,如果是只使用一次的正则,可以考虑弃之不用。

现在,我们可以来处理空格了。我们可以使用·*来表示。再度修改如下:

m/^([-+]?[0-9]+(?:\.[0-9]*)?) *([CF])$/

有人注意到哪里修改了吗?嗯……这样确实很难注意到这边有一个空格。与此同时,如果输入的是制表符(天知道为什么会输入进来),那就匹配不到了。所以,我们可以使用元字符\s来匹配空白字符,三度修改如下:

m/^([-+]?[0-9]+(?:\.[0-9]*)?)\s*([CF])$/

好了,现在只剩下小写字母的问题了。我们可以使用一个修饰符(modifier)。

结果变成这样:m/^([-+]?[0-9]+(?:\.[0-9]*)?)\s*([CF])$/i`这个修饰符只是Perl中的用法,其他语言有不同的实现方式,如Python使用的在编译的时候指定。

现在,大功告成,来测试一下:

Enter a temperature in Celsius:

33.98 c

1.10 C is 33.98 F.

结果不尽如人意……嗯,再修改一下即可。

最终版本就是这样子的:

print "Enter a temperature in Celsius:\n";

$input = ; #从用户处接受一个输入

chomp($input); #去掉换行符

if ( $input =~ m/^([-+]?[0-9]+(?:\.[0-9]*)?)\s*([CF])$/i) {

#程序运行到这里就已经匹配好了。$1保存数字,$2保存符号。

$InputNum = $1;

$type = $2;

if ($type =~ m/c/i) {

#输入为摄氏温度,计算华氏温度

$celsius = $InputNum;

$fahrenheit = ($celsius * 9 / 5) + 32;

} else {

#否则,应该是"F",那就计算摄氏温度。

$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";

}

到这里就先休息下吧。顺便来道题目思考思考:

(·*|\t*)和[·\t]*之间在匹配的结果有什么差别?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值