zoukankan      html  css  js  c++  java
  • 正则表达式实现密码检查

    起因

    起因是一个朋友问怎么实现一个密码检查功能:

    1. 密码只能由大写字母,小写字母,数字构成;
    2. 密码不能以数字开头;
    3. 密码中至少出现大写字母,小写字母和数字这三种字符类型中的两种;
    4. 密码长度8-100位

    然后他贴了写的代码:

    $value = 'A1234567890a';
    $rule = '/^[A-Z][A-Za-z]{7,100}|^[A-Z][A-Z0-9]{7,100}|^[a-z][A-Za-z]{7,100}|^[a-z][0-9a-z]{7,100}$/';
    var_dump(preg_match($rule,(string)$value));    
    

    一看这变量名以$开头,大概是php的,但我不怎么懂PHP,只看得懂中间的正则,猜最后一行是输出匹配结果。

    这个正则显然是不满足上面说的条件的,|的优先级比$低,所以前面的表达式没有控制密码长度的作用。我依稀记得曾经学正则也看过相关的题目,这种复杂的正则检查是要用零宽断言。

    然而我觉得零宽断言这东西写起来复杂,别人看更觉得复杂的东西,团队里面显然不是所有人都会去弄懂的。写这种复杂正则,以后需求变更,修改起来简直是噩梦,所以没怎么认真看,只知道有这么个东西,于是我就认真翻了一下相关的文章。

    链接如下:
    https://deerchao.net/tutorials/regex/regex.htm

    这个是我觉得写得比较好的,内容很充实的一个文章了。

    解决问题

    对于这个需求,我们先完成第124点,这三个比较简单,正则写出来如下

    var regex = /^[A-Za-z][A-Za-z0-9]{7,99}$/
    

    这个问题难写的就是第3点,如果不用零宽断言,是要用很多个|去做判断,非常长。

    第三点是

    密码中至少出现大写字母,小写字母和数字这三种字符类型中的两种;

    换个说法就是

    不能全是大写字母,不能全是小写字母,不能全是数字。

    或者说

    存在一个非大写字母,存在一个非小写字母,存在一个非数字。

    第一种

    对于第一种说法,可以用

    var regex = /^(?![A-Z]*$)/; // 不能全是大写字母
    var regex2 = /^(?![a-z]*$)/; // 不能全是小写字母
    var regex3 = /^(?![0-9]*$)/; // 不能全是数字
    

    这里的*可以换成+,只会在空字符串的时候有差异,对于我们上面的需求是没有差异的,因为限定了8-100的长度。

    与前面的正则组合起来就是

    var regex = /^(?![A-Z]*$)(?![a-z]*$)(?![0-9]*$)[A-Za-z][A-Za-z0-9]{7,99}$/;
    

    第二种

    对于第二种说法,可以用

    var regex = /^(?=.*[^A-Z])/ // 存在一个非大写字母
    var regex2 = /^(?=.*[^a-z])/ // 存在一个非小写字母
    var regex3 = /^(?=.*[^0-9])/ // 存在一个非数字
    

    这里*不能替换成+,为什么可以自己思考一下,我会给一反例证明替换有问题。

    组合起来就是

    var regex = /^(?=.*[^A-Z])(?=.*[^a-z])(?=.*[^0-9])[A-Za-z][A-Za-z0-9]{7,99}$/;
    

    如果将所有的*替换成+,那么对于Aaaaaaaa,正则匹配会失败,根据需求应该是成功。

    第三种

    什么?居然还有第三种?是的,当然还有第三种,和第四种。

    刚刚我们写的断言,按照链接给的名字是零宽度正预测先行断言(?=exp)零宽度负预测先行断言(?!exp),当然名字其实不重要的,翻译过来的名字有很多种,不同的文章也不同,也不知道哪个是比较官方的。

    所以我们可以用另外两种断言实现,分辨是零宽度正回顾后发断言(?<=exp)零宽度负回顾后发断言(?<!exp)

    对于第三种和第四种,它是从后面检查前面的断言,这种断言据说js是不支持的,但是chrome的引擎似乎是支持的,也不清楚是怎么回事。

    (?<!exp)形式写第一种说法

    var regex = /(?<!^[A-Z]*)$/; // 不能全是大写字母
    var regex2 = /(?<!^[a-z]*)$/; // 不能全是小写字母
    var regex3 = /(?<!^[0-9]*)$/; // 不能全是数字
    

    同样的*可以替换成+,组合起来就是

    var regex = /^[A-Za-z][A-Za-z0-9]{7,99}(?<!^[A-Z]*)(?<!^[a-z]*)(?<!^[0-9]*)$/
    

    第四种

    不废话了直接贴代码

    var regex = /(?<=[^A-Z].*)$/ // 存在一个非大写字母
    var regex2 = /(?<=[^a-z].*)$/ // 存在一个非小写字母
    var regex3 = /(?<=[^0-9].*)$/ // 存在一个非数字
    

    组合后如下,同样的不能把*替换成+,反例是aaaaaaaA

    var regex = /^[A-Za-z][A-Za-z0-9]{7,99}(?<=[^A-Z].*)(?<=[^a-z].*)(?<=[^0-9].*)$/;
    

    第五种、第六种……

    组合有很多种,因为条件都说的很清楚了,四种断言是可以组合的,所以其实远不止以上说的四种情况,重要的是要掌握四种断言的本质。

    结尾

    本文只是一个引子,并不是说怎么学正则,学习正则可以参考上面给的链接,这里再给一次https://deerchao.net/tutorials/regex/regex.htm

    警告:不要写过于复杂的正则,多人协作中,代码的可读性远比性能和炫技重要。

  • 相关阅读:
    Portal 并查集(去重)
    Constructing Roads 最小生成树(prime()),注意边的处理方式
    More is better 并查集最原始 最有效的形式,用rank数组,(结构体)
    【集训试题】SiriusRen的卡牌 set
    【集训试题】exam 信心考 最小割
    [leetcode] Binary Tree Pruning
    [leetcode] Daily Temperatures
    [leetcode] Sort Characters By Frequency
    [leetcode] Subdomain Visit Count
    [leetcode] Number of Boomerangs
  • 原文地址:https://www.cnblogs.com/Weilence/p/10291459.html
Copyright © 2011-2022 走看看