小骆驼书有这么一道题,看似简单,实际做起来还是要想想巧妙的办法:
写一个程序,输出有一个字母大写,而非所有字母都大写的行。它能匹配Fred,而不匹配fred 和FRED 吗。
按照上题意思,采用以下数据应该有这样的结果:
If fred ok 匹配
cute...f
Mr.fread 匹配
Fred over 匹配
FRED
Fred Fred Fred Fred
the end.
F 匹配
FFFF
fFgrd 匹配
fReD
fred
我于是编写了以下简单代码
while(<>)
{
chomp;
if(/[^A-Z]*[A-Z][^A-Z]*/)
{
print "Matched! In the line: $_ /n";
}
}
执行结果是:
FRED,Fred Fred Fred Fred,FFFF,fReD都被匹配了。
其实是对正则表达式的应用的理解出现了偏差。
正则表达式是只要待比较对象中出现了一次便算匹配,是片断匹配,而不是整体匹配。
以FRED为例,/[^A-Z]*[A-Z][^A-Z]*/的匹配结果是:
第一个[^A-Z]*匹配0次,[A-Z]匹配到F,因为F后面是R,后面的[^A-Z]*匹配0次,所以匹配成功。
正则表达式的匹配采用以上方式并不能判断“整个对象里一定没有……”的情况,只能解决“整个对象里有……”的情况。
所以转换思路,题干是“有且仅有一次”,分解为条件一“有”,条件二“且没有多于一次”。根据正则表达式的匹配特点,大写字母出现两次或者以上都会满足大写字母两次匹配,条件二可转为“且不能两次匹配大写字母”。改写代码如下:
while(<>)
{
chomp;
if(/[A-Z]/) #条件一
{
if(! /[A-Z].*[A-Z]/) #条件二
{
print "Matched! In the line: $_ /n";
}
}
}
匹配结果完全正确!
当然 ,如果采用锚定就更简单了:
while(<>)
{
chomp;
if(/^[^A-Z]*[A-Z][^A-Z]*$/)
{
print "Matched! In line: $_/n";
}
}
结果仍然正确:)