0

规则如下:
密码格式: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();
}
1

/d{3,}|[a-zA-Z]{3,}/的意思是匹配3个及以上的数字或匹配3个及以上的字母,你觉得是正确的正则吗?
正则效率低的问题:一是固定存在的解释表达式的成本;二是写出的正则效率不高,发生了太多的回溯;三是有的问题用正则已经难以表述,即便能强用正则效率却不高。对于第一点可以忽略,第二点主要看个人的能力,第三点才是主要的。
第三点的典型案例就是你提出的匹配序列,你需要判断字串是否是递增或递减,正则没办法直接做到,只能把所有的组合可能都列出来。还有就是对有效IP的匹配,需要判断是否在0-255之间,你用正则怎么玩?不也是把所有可能的组合列出来。如果我编程的话,直接把数字取出来分别判断是否在该区间内不就完事了吗?这时,你说效率差多少?

Colin_Chen · 2016年08月18日

展开评论

2个回答

3

已采纳

为什么一定要使用正则,要知道这种问题用正则要写多长,而且你写的正则也不对。自己做遍历不是更简单高效吗?

以下是伪代码,语法请忽略:)

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差来判断是否三个连续。

1
回复 wdy1184

不好意思,我理解错了。应该是说你第一个正则我完全不明白什么意思,最外层是[]+的模式,而按照 http://cn2.php.net/manual/zh/... 所给出的描述,[]应该是包含一个字符集合才对。所以我建议你多测试几个合格及不合格的数据验证这条正则的正确性。

Colin_Chen · 2016年08月18日

展开评论
-1
必须同时包含数字、大写字母,小写字母3种字符,区分大小写。

/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/

撰写答案

Planets