[免责声明,这是一个很长的答案!]
我将从字数限制开始。
您必须使用(?<!\S) 和(?!\S) 指示密码的开头和结尾,并使用\S{8,20} 作为实际密码:
(?m)(?<!\S)\S{8,20}(?!\S)
您可能已经知道(?m) 用于多行(^ 和$ 分别匹配行的开头和结尾,而不是此模式下的字符串)。
(?<!\S) 确保密码前没有非空白字符。
(?!\S) 确保密码后没有非空白字符。
现在我们添加一些限制:
不能以数字或下划线开头:(?![0-9_]) 密码开头的负前瞻:
(?m)(?<!\S)(?![0-9_])\S{8,20}(?!\S)
必须至少包含一个数字:(?=\S+[0-9]) 密码开头的正向前瞻:
(?m)(?<!\S)(?![0-9_])(?=\S+[0-9])\S{8,20}(?!\S)
必须以相同的字符结尾:您必须捕获倒数第二个字符并使用反向引用。为此,您可以将 \S{8,20} 部分更改为 \S{6,18}(\S)\1:
(?m)(?<!\S)(?![0-9_])(?=\S+[0-9])\S{6,18}(\S)\1(?!\S)
现在应该不错了。
现在到你的正则表达式:
(?m:^([^_|^0-9]{1})
首先,{1} 是多余的,因为如果您删除它,它根本不会改变任何东西。
(?m:^([^_|^0-9])
其次,你有不平衡的括号。不知道那应该是什么,但我猜第一个括号不是故意的。
(?m:^[^_|^0-9])
接下来,字符类[^_|^0-9] 匹配除_、|、^ 或范围0-9 之外的任何字符。我确定密码可以以| 或^ 开头。元字符| 在字符类中失去了意义!您可以改用:[^_0-9],这将变为:
(?m:^[^_0-9])
可以使用这个,但是你必须记住这是密码中的第一个字符;因为您要尊重的字符范围为 8 到 20 个,而它刚刚更改为 7,19。唯一剩下的就是它也接受一个空格。你可以在字符类中放一个来避免这种情况:
(?m:^[^_0-9 ])
好的,现在看起来好多了,下一个:
(?<finalTwo>(?i:\S{1}))(?i:\<finalTwo>)
首先是一个命名的捕获组,好的,具有不区分大小写模式的非捕获组(不是很有必要,因为我们在正则表达式中没有任何字母)和该非捕获组内的\S{1}。再一次,{1} 是多余的。删除它和(?i) 模式,变成:
(?<finalTwo>\S)(?:\<finalTwo>)
这还不错,如果它匹配最后两个字符,它确实可以工作。
(?=.*\d)
效果很好。您可能想要寻找 \d 匹配的 0-9 以外的字符,但如果您不介意,那几乎可以;最好在此处使用\S 而不是.,以防万一文本中有两个由空格隔开的密码,这可能会使事情变得不像您想要的那样。
(\S*)
那部分或多或少还可以。没有任何限制。
(?=\S*\d)(?m:^[^_0-9 ])(\S*)(?<finalTwo>\S)(?:\<finalTwo>)
好的,现在,请记住 (?m:^[^_0-9]) 占用一个字符,(?<finalTwo>\S)(?:\<finalTwo>) 占用两个字符,总共 3 个。因此,您施加的限制是:
(?=\S*\d)(?m:^[^_0-9 ])(\S{5,17})(?<finalTwo>\S)(?:\<finalTwo>)
它几乎可以工作,你只需要放一些东西来防止长密码的部分匹配。您通常可以使用字边界\b,但没有提到任何关于符号的内容,因此假设$@4*&AUn++ 之类的密码也被允许更安全,这就是字边界将失败的地方。这就是为什么我建议使用否定的lookarounds。