规则如下:
密码格式:6-16位数字字母组合
不包含特殊字符。
必须同时包含数字、大写字母,小写字母3种字符,区分大小写。
连续3位及以上数字不能连续(例如123、876)
连续3位及以上的字母不能连续(例如abc、cba、aaa、111、AAA)
echo !preg_match('/\d{3,}|[a-zA-Z]{3,}/', $password); echo preg_match('/\d+/', $password); echo preg_match('/[a-z]+/', $password); echo preg_match('/[A-Z]+/', $password); echo preg_match('/^([a-zA-Z0-9]){6,16}$/', $password);
以上是需求和我想出来的解决方法
但是总想能用更简洁的方式来匹配出来,希望有人能有更好的思路。
再问个问题:为什么正则表达式效率低?
因为连续的数字,字母用正则表达式判断太复杂而且效率低,所以采用逻辑代码判断的方式,以下是我用PHP写的代码,php5.5.12 初步测试成功。
<?php define('PWD_MAX_LENGTH', 16); define('PWD_MIN_LENGTH', 6); /** * @time 2016年8月29日11:52:29 */ class User_Common { /** * 密码格式:6-16位数字字母组合 * 不包含特殊字符。 * 必须同时包含数字、大写字母,小写字母3种字符,区分大小写。 * 连续3位及以上数字不能连续(例如123、876) * 连续3位及以上的字母不能连续(例如abc、cba) * @param $password * @return bool * @throws \Exception */ public static function checkPassword($password) { self::pwdLengthCheck($password); self::pwdCharValid($password); } /** * @param $password * @return bool * @throws \Exception */ private static function pwdCharValid($password) { if (!ctype_alnum($password)) { throw new Exception('不包含特殊字符', 10002); } $includeNumber = false; $includeUpperLetter = false; $includeLowerLetter = false; $length = strlen($password); for ($i=0; $i < $length; $i++) { $char = $password[$i]; $includeUpperLetter = (!$includeUpperLetter && ctype_upper($char)) ? true : $includeUpperLetter; $includeNumber = (!$includeNumber && ctype_digit($char)) ? true : $includeNumber; $includeLowerLetter = (!$includeLowerLetter && ctype_lower($char)) ? true : $includeLowerLetter; if ($i != 0 && !empty($password[$i+1]) && abs(ord($password[$i]) - ord($password[$i-1])) <=1 && ord($password[$i]) - ord($password[$i-1]) == ord($password[$i+1]) - ord($password[$i])) { throw new Exception('连续3位及以上数字或字母不能连续(例如123、876)', 10004); } } if ($includeLowerLetter && $includeNumber && $includeUpperLetter) { return 2; } else { throw new Exception('必须同时包含数字、大写字母,小写字母3种字符,区分大小写', 10003); } } private static function pwdLengthCheck($password) { if (strlen($password) > PWD_MAX_LENGTH || strlen($password) < PWD_MIN_LENGTH) { throw new Exception('密码格式:6-16位数字字母组合', 10001); } } /** * 检查是否为1开头的11位数字手机号 * @param int $phoneNumber * @return boolean 是否匹配 */ public static function checkPhoneNumber($phoneNumber) { return preg_match('/^1\d{10}$/', $phoneNumber); } } try { var_dump(User_Common::checkPassword('AbA001')); } catch (Exception $e) { echo $e->getMessage(); }
三叔2016-11-10 15:23:32
为什么一定要使用正则,要知道这种问题用正则要写多长,而且你写的正则也不对。自己做遍历不是更简单高效吗?
以下是伪代码,语法请忽略:)
password = getPassword() //获取密码 if (password.length < 6 || password.length > 16) throw 长度错误; //判断长度 hasNumber = false hasUpper = false hasLower = false preDiff = 0 preChar = null for (char in password) { //遍历取出字符 if (!hasNumber) hasNumber = isNumber(char); //判断是否包含数字 if (!hasUpper) hasUpper = isUpper(char); //判断是否包含大写字母 if (!hasLower) hasLower = isLower(char); //判断是否包含小写字母 if (isSpecial(char)) throw 包含特殊字符; //判断是否包含特殊字符 diff = getDiff(preChar, char); //获取字符的ASCII码与上一个字符的差 //如果preDiff与diff差都为-1或1,则认为连续 if (abs(preDiff) == 1 && preDiff == diff) throw 字母或数字连续错误; //可单独判断char求是字母还是数字连续 preChar = char; preDiff = diff; }
在上例代码中
6-16位数字字母组合
不包含特殊字符。
必须同时包含数字、大写字母,小写字母3种字符,区分大小写。
应该都都看懂怎么判断的,至于是否连续主要是利用字符的ASCII码,并且0-9、a-z、A-Z在ASCII中是连续的。保存前两个字符ASCII差与当前字符ASCII差来判断是否三个连续。