Logo
Section 1 / 6

密码模式匹配

让我们来看看解决密码模式匹配问题所需的规则和条件。

导入

密码模式匹配

让我们来看看解决密码模式匹配问题所需的规则和条件。

如果你已经读到这里,希望你现在已习惯用正则表达式的思维方式思考问题,并且理解了正则表达式所带来的强大功能。这正是它们在各类软件项目中被广泛使用的原因。

在本章最后,我们将展示几个真实场景中的应用实例,说明如何仅用少量代码,通过正则表达式实现非常实用的功能。

密码规则验证

密码模式匹配可能是正则表达式最简单的应用场景之一。毕竟,密码规则有时会显得相当复杂。你是否曾多次在注册网站时看到类似这样的提示信息:“你的密码长度需在6到12个字符之间,且至少包含一个大写字母、一个小写字母、一个特殊符号和一个数字,最好还能组合出你自己一辈子都记不住的字母组合”?

当然,最后一句可能源自我们对这类密码规则的无奈吐槽。但前半部分是真实的。没错,不使用正则表达式也能实现这些规则的校验逻辑,但那样就失去乐趣了。

尽管从表面上看,一个正则表达式要检查这么多条件似乎很复杂,但我们实际上会使用一系列正向先行断言(如果忘了,请回顾第2章关于量词的内容)来分别检查每一条条件。

本质上,我们需要满足五个条件:

  1. (cond1):至少包含一个数字。
  2. (cond2):至少包含一个小写字母。
  3. (cond3):至少包含一个大写字母。
  4. (cond4):至少包含一个特殊符号。
  5. (cond5):长度在6到12个字符之间。

如果我们用伪代码来表示这个校验逻辑,会是这样:

1 IF (cond1 AND cond2 AND cond3 AND cond4 AND cond5) THEN 2 // 你的密码有效 3 END

这对应的实际正则表达式如下:

1 ^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@_&%$"!]).{6,12}$

下面我们逐步解析它的含义。

先行断言

首先要理解的是,这里使用的正向先行断言起到了“屏障”的作用。每个先行断言都在检查前四个条件中的某一项,一旦其中任何一个失败,正则引擎就会停止匹配并返回不匹配的结果。

另一个值得注意的细节是这些检查的写法。在第2章中,我们介绍的形式是 XX?=YY,意思是只有当 XX 后面跟着 YY 时才匹配。但在这里,我们写的是 ?=YY。区别在哪里?

省略了前面的 XX 部分,意味着我们只会在当前位置(即字符串开头,第一个字符之前)匹配空位,前提是它后面紧跟着 YY。而在这个例子中,YY 是 .* 加上一个字符类。这等价于“任意字符(包括没有字符),后接指定的字符类”。现在明白了吗?

  • (?=.*\d) 的含义是:我在当前空位置进行匹配,只要其后跟随任意数量的字符(包括零个),接着出现一个数字(即:该字符串中至少包含一个数字)。
  • (?=.*[a-z]) 的含义是:我在当前空位置进行匹配,只要其后跟随任意数量的字符(包括零个),接着出现一个小写字母(即:该字符串中至少包含一个小写字母)。
  • (?=.*[A-Z]) 的含义是:我在当前空位置进行匹配,只要其后跟随任意数量的字符(包括零个),接着出现一个大写字母(即:该字符串中至少包含一个大写字母)。
  • (?=.*[@_&%$"!]) 的含义是:我在当前空位置进行匹配,只要其后跟随任意数量的字符(包括零个),接着出现以下任一字符:@、_、&、%、$、" 或 !(即:该字符串中至少包含一个特殊符号)。

字符串长度

当以上四个条件全部满足后,引擎才会进入 .{6,12} 这一部分。这部分表示任意字符序列,长度必须在6到12之间。单独来看,这并不安全,因为任何长度符合要求的字符串都会匹配成功。但因为我们前面已经添加了所有先行断言,所以可以确保:只有当所有条件都满足时,引擎才会到达这一部分。

整体匹配

最后,为了确保匹配结果不是更大无效字符串中的部分匹配,我们必须保证整个输入是一个完整的单词。为此,我们在正则表达式前后加上 ^ 和 $,分别代表字符串的开始和结束。这样就能确保匹配的是完整字符串,而非其中的一部分。

用正则表达式思考

检查表达式