问题背景
最近帮共事解决了一个正则问题,挺有意思,分享给大家
背景是共事在做一个用户注册相干的性能,甲方提出了一些对明码复杂度的要求,要求长度 8 -32 位,小写字母,大写字母,数字,符号四种必须都有,上面是匹配明码的最后的正则表达式
(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])(?=.*[^a-zA-Z0-9]).{8,32}
问题验证
大家能够看一下这个正则,感觉一下用这个正则可能满足下面的明码校验要求吗?能够花一点工夫想一想
答案是:不能
咱们来做个简略的测试,倡议找一个在线工具,理论测试一下
把表达式输出进去,咱们测如下几段文本
- abc123A:没有匹配,长度不够
- abcd12345:没有匹配,复杂度不够,没有大写和符号
- abcdA123-:可能失常匹配,长度合乎,复杂度合乎
那这不是阐明这个正则是 OK 的吗?咱们再测上面这个文本
- abcdA123- 我:这怎么也匹配了
- abc 他 dA★123:这怎么也匹配了
问题改良
这个正则外面有两个小问题
- 符号限定过于宽泛:(?=.*[^a-zA-Z0-9])这个判非就把其余所有字符都囊括进来了
- 没有输出文本的限定:.{8,32}这个点也囊括了除换行以外的所有字符
针对下面两个问题改良一下这个正则,首先要枚举咱们容许呈现的符号,假如咱们只容许呈现“-_”这两个字符
改良后的正则如下
^(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])(?=.*[-_])[0-9a-zA-Z-_]{8,32}$
这个问题一方面是不足测试验证,但更深层的起因其实是对正则不够理解,不能疾速发现正则的问题,下一节咱们就来看看这个正则外面比拟艰难的局部
扩大浏览
可能有同学有疑难后面这么一大堆括号是干什么的
这里温习一下正则表达式的一个根底概念:零宽断言
零宽断言 是一种零宽度的匹配, 它匹配到的内容不会保留到匹配后果中去, 最终匹配后果只是一个地位而已。
(?=)就是一种零宽断言,叫做正向后行断言,次要作用是匹配表达式前面的内容
比方:\d(?=px),这个正则能匹配到 1px 中的 1,不会匹配到 2pm 中的 2,因为 2 前面跟的不是 px,如果想同时把 px 也匹配上,那就得写 \d(?=px)px,这看起来就跟 \dpx 一样了,在匹配单个条件的时候确实没有区别,但匹配多条件的时候就不一样了
咱们还是用例子来解释一下
[a-z0-9]{4,10}
这样一个正则能示意英文小写和数字呈现 4 -10 次,但不能要求必须同时有英文和数字,能够是纯英文,也能够是纯数字
正则是一种程序性的描述性语言,1a 和 a1 是不同的,如果想形容这种不确定地位的组合,对于一般正则写法来说是比拟艰难的,根本就变成了排列组合了
这时咱们就能够通过正向后行断言来帮忙进行匹配,正向后行断言能够了解成对字符串的模式匹配
(?=.*[a-z])
示意前面必须呈现一个英文小写,然而地位能够不定,应用.* 来表白地位不定这个信息
同理
(?=.*[0-9])
示意前面必须呈现一个数字,然而地位能够不定
[a-z0-9]{4,10}限定输出内容和长度
(?=.*[a-z])(?=.*[0-9])[a-z0-9]{4,10}
连贯在一起就能示意英文小写和数字呈现 4 -10 次,且英文和数字都必须至多呈现一次