正则表达式接受 4 条规则中的 3 条

Regex to accept 3 out of 4 rules

我似乎无法使正则表达式正确满足以下要求:长度在 8 到 20 之间的字符串必须包含至少 1 个大写字母字符、至少 1 个小写字母字符和至少 1 个数字或至少 1 个特殊字符(或两者)。假设特殊字符仅限于包括@、#、&、~。

我最初是这样写的:

^(?=.*?[A-Z])(?=.*?[a-z])(?=(.*?[0-9])|(.*?[@#&~])).{8,20}$

正如预期的那样,它成功匹配了 5abcdefG、Abc@defghi、5abcdefG~ 等字符串

问题是它允许我提到的 4 个特殊字符以外的字符。因此像 1€abcdefG 和 Abc!defghi 这样的字符串也匹配,但它们不应该匹配。我错过了什么?

重点是你的 . 匹配除换行符以外的任何字符,因此它可以匹配除你的 4 个特殊字符、字母或数字之外的许多字符。

此外,将 OR 条件拆分为 2 个具有先行 ((?=(.*?[0-9])|(.*?[@#&~]))) 的替代分支是没有意义的。您可以将该条件合并为一个 (?=.*?[0-9@#&~])。重点是正字符class里面的ranges/chars是"OR'ed",[0-9@#&~]匹配一个数字,或者@,或者#,或 &,或 ~.

我建议

^(?=[^A-Z]*[A-Z])(?=[^a-z]*[a-z])(?=[^0-9@#&~]*[0-9@#&~])[A-Za-z0-9@#&~]{8,20}$

this regex demo

您还可以使用注释模式或块来构建动态模式,使模式具有可读性和可维护性:

^                           # start of string
  (?=[^A-Z]*[A-Z])          # string must have an uppercase letter
  (?=[^a-z]*[a-z])          # string must have a lowercase letter
  (?=[^0-9@#&~]*[0-9@#&~])  # string must have a digit or defined special char
  [A-Za-z0-9@#&~]{8,20}     # The string should have 8 to 20 symbols from the defined set
$                           # end of string

[A-Za-z0-9@#&~] 将只允许您在此字符中指定的字母、数字和特殊字符 class。

这个正则表达式也符合对比原则(前瞻失败或匹配否定字符更快classes)。

这里的简单答案是不要使用单个正则表达式。这将简化一切:

  • 8 到 20 个字符:每种语言都提供了获取字符串长度的标准方法。使用它并检查数字。
  • 包含一个大写字母:检查它是否匹配 [A-Z]。您可能需要修改它以实现国际化。
  • 包含一个小写字母:检查它是否匹配 [a-z]。您可能需要修改它以实现国际化。
  • 包含一个数字:检查它是否匹配 [0-9].
  • 包含特殊字符:检查它是否匹配 [@#&~].
  • 仅包含允许的字符:使其匹配 ^[A-Za-z0-9@#&~]+$。 (这似乎是一个可疑的要求,特别是如果这是针对密码的。)

在最后两个检查周围会有一些额外的条件,只需要一个,但这没什么大不了的。

最重要的是,没有人能够为此阅读单个正则表达式。您必须记录它所做的一切,每个接触该正则表达式的开发人员要么讨厌您,要么像我在此处描述的那样将其重新实现为多重检查。 停止。严重地。这是 "parse HTML with regex" 级别的糟糕设计。只需使用多次检查。这是最明智的做法。

最重要的是,以后添加新要求会容易得多,如果您 运行 进入无法通过正则表达式检查的内容,则无论如何都必须这样做。