【问题标题】:How to check whether a string contains lowercase letter, uppercase letter, special character and digit?如何检查字符串是否包含小写字母、大写字母、特殊字符和数字?
【发布时间】:2012-02-06 08:56:42
【问题描述】:

我一直在谷歌上搜索,但没有找到我的问题的答案:

如何使用正则表达式检查字符串是否至少包含以下各项:

  • 大写字母
  • 小写字母
  • 数字
  • 特殊字符:~`!@#$%^&*()-_=+\|[{]};:'",<.>/?

所以我至少需要一个大写字母至少一个小写字母至少一个数字至少一个特殊字符。

我确定答案很简单,但我找不到。非常感谢任何帮助。

【问题讨论】:

  • Google 搜索“用于强密码验证的正则表达式”

标签: java regex


【解决方案1】:

正则表达式不适用于需要满足所有几个条件的测试。

因此,最简单的答案是不要同时尝试测试它们,而只是依次尝试四个类中的每一个。

您的代码可能会部分地慢一些,但它会更容易阅读和维护,例如

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;
}

EDIT 固定引号 - 做了太多该死的 JS 编程...

【讨论】:

  • @AlanMoore 我给了你一个 +1 错误...确实.matches() 在正则表达式方面被严重错误命名,并希望匹配整个输入,但解决方案是 not 使用.*。真正的解决方案是改用.find(),它执行真正的正则表达式匹配。
  • @fge:不幸的是,String 没有find() 方法,我认为在pass.matches(".*[A-Z].*") 完成工作时告诉人们使用Pattern.compile("[A-Z]").matcher(pass).find()) 没有任何意义。唯一需要注意的是字符串中是否有行分隔符(\n\r 等),我认为这不是问题。
  • 如果您愿意,这也将允许您向用户返回特定于条件的错误消息。
【解决方案2】:

这可以在 java 中作为单个正则表达式完成您想要的操作,尽管我个人会使用类似于 Mark Rhodes 提供的解决方案。随着规则变得越来越复杂,这将很快变得荒谬(如果还没有......)。

String regex = "^(?=.*?\\p{Lu})(?=.*?[\\p{L}&&[^\\p{Lu}]])(?=.*?\\d)" + 
               "(?=.*?[`~!@#$%^&*()\\-_=+\\\\\\|\\[{\\]};:'\",<.>/?]).*$"
  1. ^ 这匹配字符串的开头。这并不是绝对必要的,但我发现它有助于可读性和理解。此外,在可以的情况下使用它通常会带来很大的性能提升,而且几乎不会造成任何损失。

  2. (?=X) 这称为正向预测。基本上我们所说的是“字符串的开头 (^) 必须跟在这个东西 X 之后才能匹配,但是 不要 将光标移动到X 的末尾,留在行首。(即“向前看”部分。)

  3. .*?\p{Lu} 吃掉行首之后的字符,直到找到大写字母。如果没有找到大写字母,这将无法匹配。我们使用 \p{Lu} 而不是 A-Z,因为我们不希望世界其他地方的人举手抱怨我们的软件是由一个无知的美国人编写的。

  4. 现在我们回到行首(我们返回是因为我们使用了前瞻)并开始搜索 .*?[\p{L}&&[^\p{Lu}]] 简写对于“所有字母,减去大写字母”(因此匹配小写)。

  5. .*?\d + .*?[`~!@#$%^&*()\-_=+\\\|\[{\]};:'\",/?] 重复数字和特殊字符列表。

  6. .*$ 匹配其他所有内容,直到行尾。我们这样做只是因为 java 中“匹配”方法的语义,它查看 entire 字符串是否与正则表达式匹配。您可以离开这部分并使用 Matcher#find() 方法并获得相同的结果。

  7. The Owl is one of the best books ever written on any technical subject. 阅读起来又短又快。我不能推荐它。

【讨论】:

  • +1 谢谢你,Affe,你能给我解释一下吗?我必须承认我不是正则表达式专家,我只是想用正则表达式解决我的问题,而不是逐个字符地检查我的字符串。
  • 谢谢 Affe,我想再次为你投票,但我不能。好的,我可以为你的评论点赞。
【解决方案3】:

我同意@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 次时,上述代码的速度是原来的两倍。虽然,我想现在你可以说这段代码更难阅读了!没关系!

【讨论】:

  • +1。这完全取决于您的要求,不是吗。硬件限制总是考虑在内,任何在设计中没有考虑到它们的人最终都会付出代价,无论是因为单个 SQL 查询的三天运行时间导致应用程序崩溃,还是像不得不购买一样微妙比其他方式早三个月获得更大的部分结肠。
【解决方案4】:

因为这些不会以任何特定顺序出现,所以您需要为每个必需的字符类进行前瞻断言:

(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[~!@#$%\^&*()\-_=+\|\[{\]};:'",<.>/?])

(注意:因为反斜杠、插入符号、连字符和方括号在范围内可能是特殊的,所以如果它们出现在范围内,则应将它们转义,如第四个前瞻断言所示。)

如果您的正则表达式变体支持x 修饰符,则可以使用空格和 cmets 使此构造更具可读性。在java.util.regex,你可以这样做:

(?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

【讨论】:

  • 这证明了我的观点 - 我一直在使用正则表达式,天知道有多少年了,从来没有使用过前瞻断言,并且很难阅读这个。任何人都可以阅读带有四个简单正则表达式的版本。
  • (+1) 好答案。只要确保使用全局匹配(无论如何这是最常见的)。
  • @Alnitak 我现在已经说明了扩展语法的使用,这使得表达式易于阅读并且受到 OP 语言的支持。我倾向于在示例中避免它,只是为了使我的表达式更普遍有用,即使是没有扩展语法的正则表达式实现。
  • 蒂姆说得对。另外,你是如何应用正则表达式的?如果您使用matches() 方法,则需要使用.* 或其他东西完成正则表达式,以使其消耗整个字符串。
  • 原始问题未提供上下文。例如,它并没有说正则表达式将“按原样”交给matches()。 OP 很可能希望将解决方案与matcher() 一起使用,或者作为更大正则表达式的一部分。所以我只是提供将执行必要断言的片段。如果想要的答案更具体,我可以修改代码以满足特定需求。
【解决方案5】:

您正在寻找character classes

  • 大写字母:[A-Z]
  • 小写字母:[a-z]
  • 数字:[0-9] 或 \d
  • 特殊字符:[^A-Za-z0-9](也就是说,不是任何其他字符,^ 否定类)

如果您想测试“this”或“that”,您可以组合这些范围。例如,大小写字母为[A-Za-z]

【讨论】:

  • OP 不是在寻找 'this' or 'that',而是寻找 'this' and 'that'。我想不出单独使用字符类的方法。
  • +1@MetaEd 是的,MetaEd,我正在寻找至少一个大写字母和一个小写字母,至少一个数字和一个特殊字符。
  • 这不是一个完整的答案,但至少链接是相关的,这就是我没有删除它的原因。
【解决方案6】:

\w: 用于匹配字母数字(字母可大可小)
\W: 用于匹配特殊字符

我认为这个 RegEx 会对你有所帮助:

[\w|\W]+

这是一个很好的RegEx Simulator,你可以用它来构建你自己的RegEx。

【讨论】:

  • -1。 “\W 匹配特殊字符”充其量只是误导。 \W\w 的补码,所以 [\w\W] 匹配任何单词字符,或任何不是单词字符的字符——即任何字符。 (没有必要在字符类中指定“或”,所以| 匹配文字|——并且已经被\W 匹配。)无论如何,你错过了问题的重点,即确保存在 四种字符中的每一种
  • +1@Alan Moore 是的,MetaEd,我正在寻找至少一个大写字母和一个小写字母,至少一个数字和一个特殊字符。我将编辑我的帖子以包含“其中一个”而不是“所有这些中的一个”以避免混淆
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-13
  • 2018-02-03
  • 1970-01-01
  • 1970-01-01
  • 2013-12-07
  • 2016-03-02
  • 1970-01-01
相关资源
最近更新 更多