我已经谷歌了很多,但我没有找到答案:
如何使用正则表达式检查一个字符串是否至少包含以下四个中的每一个:
- 大写字母
- 小写字母
- 数字
- 特殊字符:
~`!@#$%^&*()-_=+\|[{]};:'",<.>/?
所以我需要至少一个大写字母和至少一个小写字母和至少一个数字和至少一个特殊字符。
我相信答案非常简单,但我找不到它。任何帮助都将不胜感激。
正则表达式不太适合测试需要同时满足多个条件的情况。
因此,最简单的答案就是不要尝试同时测试所有条件,而是依次测试这四个类。虽然你的代码可能会稍微慢一点,但它会更易于阅读和维护,例如:
public boolean isLegalPassword(String pass) {
if (!pass.matches(".*[A-Z].*")) return false;
if (!pass.matches(".*[a-z].*")) return false;
if (!pass.matches(".*\\d.*")) return false;
if (!pass.matches(".*[~!.......].*")) return false;
return true;
}
编辑修正引号-最近一直在写JS编程...
.matches()
方法在正则表达式方面的命名确实非常不准确,它期望匹配整个输入,然而解决方法并_不是_在两侧使用 .*
。真正的解决方法是使用 .find()
方法,它可以执行真正的正则表达式匹配。 - fgefind()
方法。我认为告诉人们使用Pattern.compile("[A-Z]").matcher(pass).find())
并没有意义,因为pass.matches(".*[A-Z].*")
就可以完成任务。需要注意的唯一一件事是如果字符串中有换行符(\ n
,\ r
等),但我认为这在这里不是问题。 - Alan Moore我同意@Alnitak的答案最易于阅读,但它存在一个问题,即每次运行时都必须评估正则表达式。既然正则表达式是固定的,那么编译它们然后进行比较就是有意义的。 例如:
private static final Pattern [] passwordRegexes = new Pattern[4];
{
passwordRegexes[0] = Pattern.compile(".*[A-Z].*");
passwordRegexes[1] = Pattern.compile(".*[a-z].*");
passwordRegexes[2] = Pattern.compile(".*\\d.*");
passwordRegexes[3] = Pattern.compile(".*[~!].*");
}
public boolean isLegalPassword(String pass) {
for(int i = 0; i < passwordRegexes.length; i++){
if(!passwordRegexes[i].matcher(pass).matches())
return false;
}
return true;
}
当对一个长度为10的密码运行100,000次时,以上代码的速度是原来的两倍。虽然我猜现在你可能会说这段代码更难以阅读了!不过没关系!
这个单一的正则表达式可以在java中完成你想要的事情,尽管我个人会使用像Mark Rhodes提供的解决方案那样的东西。随着规则变得更加复杂,这将很快变得荒谬(如果还没有的话…)。
String regex = "^(?=.*?\\p{Lu})(?=.*?[\\p{L}&&[^\\p{Lu}]])(?=.*?\\d)" +
"(?=.*?[`~!@#$%^&*()\\-_=+\\\\\\|\\[{\\]};:'\",<.>/?]).*$"
^ 表示匹配字符串的开头,这不是必需的,但我发现这有助于提高可读性和理解性。而且,在可以使用它时,使用它通常会带来很大的性能改进,并且几乎从不影响效率。
(?=X) 这被称为正向先行断言。基本上,我们在说“必须以这个东西X紧随其后,才能匹配字符串的开头(^),但是不要将光标移动到X的末尾,仍然停留在行首(这就是“向前查看”的部分)。”
.*?\p{Lu} 匹配行首之后的字符,直到找到一个大写字母。如果没有找到大写字母,这个表达式将无法匹配成功。我们使用\p{Lu}而不是A-Z,因为我们不希望其他地区的人抱怨我们的软件是由无知的美国人编写的。
现在我们回到行首(因为我们使用了 lookahead),开始搜索.*?[\p{L}&&[^\p{Lu}]] 的缩写,表示“所有字母,减去大写字母”(因此与小写字母匹配)。
.*?\d + .*?[`~!@#$%^&*()\-_=+\\\|\[{\]};:'\",<.>/?] 用于匹配数字和一系列特殊字符的表达式。
.*$ 匹配行尾之前的所有内容。我们这样做只是因为java中的“matches”方法语义会检查整个字符串是否与正则表达式完全匹配。您可以省略此部分并使用Matcher#find()方法,得到相同的结果。
The Owl是任何技术主题上撰写的最好的书之一。它很短而且阅读速度很快。我强烈推荐。
(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[~!@#$%\^&*()\-_=+\|\[{\]};:'",<.>/?])
(?x) # extended syntax
(?=.*[A-Z]) # look ahead for at least one upper case
(?=.*[a-z]) # look ahead for at least one lower case
(?=.*[0-9]) # look ahead for at least one numeral
(?=.*[~!@#$%\^&*()\-_=+\|\[{\]};:'",<.>/?])
# look ahead for at least one of the listed symbols
matches()
方法,你需要在正则表达式的末尾加上.*
或其他内容,以使其消耗整个字符串。 - Alan Moorematches()
。 OP很可能想要使用解决方案与matcher()
一起使用,或作为更大正则表达式的一部分。因此,我只是提供执行必要断言的片段。如果所需答案更具体,我可以修改代码以适应特定需求。 - MetaEd您正在寻找字符类。
^
表示否定该类)如果你想测试'this'或者'that',你可以结合这些范围。例如,大写或小写字母可以用[A-Za-z]
表示。
\W
匹配特殊字符”最多是误导性的。\W
是\w
的补集,因此[\w\W]
匹配任何单词字符或任何非单词字符,即任何字符。(在字符类中不需要指定“OR”,因此|
匹配文字|
——这已经被\W
匹配了。)无论如何,您忽略了问题的要点,即确保每种四种字符中的每一种都存在。 - Alan Moore