正则表达式恰好匹配n次出现的字母和m次出现的数字

时间:2015-11-18 10:06:13

标签: java regex

我必须匹配一个8个字符的字符串,它可以包含2个字母(1个大写和1个小写),正好是6个数字,但它们可以任意排列。

所以,基本上:

  • K82v6686将通过
  • 3w28E020将通过
  • 1276eQ900会失败(太长时间)
  • 98Y78k9k会失败(三个字母)
  • A09B2197将失败(两个大写字母)

我尝试使用正向前瞻来确保字符串包含数字,大写和小写字母,但是我将其限制为一定数量的出现时遇到了麻烦。我想我可以通过包括字母和数字可能出现的所有可能组合来解决这个问题:

(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z]) ([A-Z][a-z][0-9]{6})|([A-Z][0-9][a-z][0-9]{5})| ... | ([0-9]{6}[a-z][A-Z])

但这是一种非常迂回的做法,我想知道是否有更好的解决方案。

3 个答案:

答案 0 :(得分:8)

您可以使用

^(?=[^A-Z]*[A-Z][^A-Z]*$)(?=[^a-z]*[a-z][^a-z]*$)(?=(?:\D*\d){6}\D*$)[a-zA-Z0-9]{8}$

请参阅regex demo(由于多线输入而稍有修改)。在Java中,不要忘记使用双反斜杠(例如\\d来匹配数字)。

以下是细分:

  • ^ - 字符串的开头(假设不使用多行标志)
  • (?=[^A-Z]*[A-Z][^A-Z]*$) - 检查是否只有1个大写字母(使用\p{Lu}匹配任何Unicode大写字母和\P{Lu}以匹配除此之外的任何字符)
  • (?=[^a-z]*[a-z][^a-z]*$) - 类似检查是否只有1个小写字母(或者,使用\p{Ll}\P{Ll}来匹配Unicode字母)
  • (?=(?:\D*\d){6}\D*$) - 检查字符串中是否有六位数字(=从字符串的开头,可以有0个或更多的非数字符号(\D匹配任何字符,但是一个数字,您也可以将其替换为[^0-9]),然后是数字(\d),然后是0或更多非字母字符(\D*),直到字符串结尾( $))然后
  • [a-zA-Z0-9]{8} - 完全匹配8个字母数字字符。
  • $ - 字符串结束。

遵循逻辑,我们甚至可以将其简化为

^(?=[^a-z]*[a-z][^a-z]*$)(?=(?:\D*\d){6}\D*$)[a-zA-Z0-9]{8}$

可以删除一个条件,因为我们只允许使用[a-zA-Z0-9]的小写和大写字母和数字,当我们应用2个条件时,当匹配字符串时,第3个条件会自动执行(在这种情况下,一个字符必须是大写的。)

将它与Java matches()方法一起使用时,不需要在模式的开头和结尾使用^$个锚点,但在前瞻中仍然需要它:

String s = "K82v6686";
String rx = "(?=[^a-z]*[a-z][^a-z]*$)" +      // 1 lowercase letter check
            "(?=(?:\\D*\\d){6}\\D*$)" +       // 6 digits check
            "[a-zA-Z0-9]{8}";                 // matching 8 alphanum chars exactly
if (s.matches(rx)) {
    System.out.println("Valid"); 
} 

答案 1 :(得分:1)

Pattern.matches(".*[A-Z].*", s) &&
Pattern.matches(".*[a-z].*", s) &&
Pattern.matches(".*(\\D*\\d){6}.*", s) &&
Pattern.matches(".{8}", s)

由于我们需要为此任务创建交替自动机,因此使用正则表达式的组合来构造字符类型会更加简单。

我们要求它至少有一个小写字母,一个大写字母和6个数字,这三个类是互斥的。在最后一个条件下,我们要求字符串的长度恰好是这些数字的总和,这样就不会留出超出所需类型的额外字符的空间。当然,我们可能会说s.lenght() == 8为最后一个条件,但这会打破风格:)。

答案 2 :(得分:0)

词法排序字符串然后匹配^(?:[a-z][A-Z]|[A-Z][a-z])[0-9]{6}$