正则表达式论坛上,有人问了这样两个问题(原贴在这里):

  • 问题1: 密码验证:由且仅由数字、字母(大小写)、特殊符号(@ % &…)组成,三者缺一不可,密码不少于8位。
  • 问题2: 十位的数字、字母组合密码,其中包含4位数字和6位字母。

感兴趣的话,建议您在读下文之前,自己思考一下解法,以免被我的思路干扰。

Stage0

这两个问题其实都是一个路子:对于一段字符串,有多个并列的限定条件。

对于问题1,它需要满足的条件如下:

  • 8位以上;
  • 必须包含1位以上的数字;
  • 必须包含1位以上的字母;
  • 必须包含1位以上的特殊字符。

对于这样的要求,简单使用[0-9a-za-Z@%&]{8,}来匹配的。因此它也匹配像00000000、1111aaaaa这样只含一种或两种字符的字符串。因此,我们要加上更为严格的限制条件,以便匹配更精确。

Stage1

数字必须出现一次,则对于每个字符位置来说,它应该是这样的:

代码:

[0-9a-zA-Z@%&]+\d

字母必须出现一次,则对于每个字符位置来说,它应该是这样的:

代码:

[0-9a-zA-Z@%&]+[a-zA-Z]

特殊字符必须出现一次,则对于每个字符位置来说,它应该是这样的:

代码:

[0-9a-zA-Z@%&]+[@%&]

这三个条件必须同时满足,因此:
代码:

(?=[0-9a-zA-Z@%&]+\d)(?=[0-9a-zA-Z@%&]+[a-zA-Z])(?=[0-9a-zA-Z@%&]+[@%&]).{8,}

为了保证字符整行匹配,需要加上条件^$:
代码:

^(?=[0-9a-zA-Z@%&]+\d)(?=[0-9a-zA-Z@%&]+[a-zA-Z])(?=[0-9a-zA-Z@%&]+[@%&]).{8,}$

它匹配的是,8位(包括)以上字符,由且仅由数字、字母和特殊字符组成。

Stage2

stage1 中的解法,已经可以匹配所需要的结果了。但是,与stage0 代码[0-9a-za-Z@%&]{8,}相反,它只能匹配部分合乎条件的字串,同时会漏掉另一外一些情况。看图:

我爱正则表达式|两条与密码验证相关的正则表达式问题

上图中Test部分中彩色部分为正则表达所匹配的字串。但是前三条是符合要求的,却不被匹配。之所以会出现这样的情况,是因为在环视条件中使用了+量词,这会将本来用作辅助验证的字符被消耗掉,原本合格的字串被误认为不合格了。

问题出在+上,因此我们使用*量词,这样就好多了。正则表达式为:

^(?=[0-9a-zA-Z@%&]*\d)(?=[0-9a-zA-Z@%&]*[a-zA-Z])(?=[0-9a-zA-Z@%&]*[@%&]).{8,}$

匹配效果如下所示:

我爱正则表达式|两条与密码验证相关的正则表达式问题

Stage3

但是问题依然存在。测试发现,像这样的字串也是匹配的,但是它显然不是合格的密码字串:

Photobucket

之所以出现这样的问题,是因为stage2代码中

.{8,}$

前边千辛万苦使用[0-9a-zA-Z@%&]所界定的条件,在这里轻轻松松被破坏了。stage2其实只管前8位,只要前8位字符符合要求,它就认为万事大吉了。

认识到这一点,我写个一条长长的正则式:

^(?=[0-9a-zA-Z@%&]*\d)(?=[0-9a-zA-Z@%&]*[a-zA-Z])(?=[0-9a-zA-Z@%&]*[@%&])[0-9a-zA-Z@%&]{8,}$

但是这条正则表达太复杂了。能不能短一些呢?当然可以。从上文可以看出,前边其实不必界定太复杂的条件,只要在最后加上条件判断即可。因此,正则表达式可以改为:

^(?=.*\d)(?=.*[a-zA-Z])(?=.*[@%&])[0-9a-zA-Z@%&]{8,}$

这样一来,我们就得到了这道题迄今为止最简洁的解法。

同理可得,第二道题的解法是:

^(?=.*\d)(?=.*[a-zA-Z])(?=.*[@%&])[0-9a-zA-Z@%&]{8,}$

不多解释。

在思考本题的过程中,感谢创亿无限在stage2的测试,感谢余晟老师在stage3中的指点。余老师现在正写一本正则表达式的傻瓜书,请点击余晟老师的博客来探寻详情。