前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >小课堂|密码至少包含2种字符组合以上

小课堂|密码至少包含2种字符组合以上

作者头像
孟君
发布2021-10-13 11:41:38
4.5K0
发布2021-10-13 11:41:38
举报
文章被收录于专栏:孟君的编程札记

在平时开发时,经常会有格式要求的判断,比如密码格式要求:

  • 长度为6-16个字符
  • 字母(不分大小写)或数字或特殊字符(*、$、@、!、#、?)至少包含其中2种

面对这样的一个判断要求,我们该如何实现呢?本文给出不使用正则和使用正则表达式两种解决方法,其中掌握正则的套路步骤可以较好的理解,去应用不同的场景。

1、非正则实现

思路

1、判断字符串是否为空

2、判断长度

3、定义三个变量,判断对应的字符是否找到

代码语言:javascript
复制
    int alphabeticFound = 0;
    int digitalFound = 0;
    int specialCharFound = 0;

依次判断字符是否满足字母、数字或者特殊字符。如满足符合要求的字符,则相关变量赋值为1;如不符合则直接返回false,表示出现不符合要求的字符。

4、判断三个变量找到的情况,累加大于1,则表示至少满足2种字符

代码实现

代码语言:javascript
复制
import java.util.Arrays;
import java.util.List;

public class PasswordValidateExample {

  //特殊字符列表
  private static final List<Character> SPECIAL_CHARS = Arrays.asList('_','*','@','!','#','%','?','$');

  public static boolean isMatched1(String value) {
    if(value == null) {
      return false;
    }
    //检查长度
    int len = value.length();
    if(len <6 ||len >16) {
      return false;
    }
    int alphabeticFound = 0;
    int digitalFound = 0;
    int specialCharFound = 0;
    for(char c: value.toCharArray()) {
      if(Character.isAlphabetic(c)) {
        alphabeticFound = 1;
      }else if (Character.isDigit(c)) {
        digitalFound = 1;
      }else if(SPECIAL_CHARS.contains(c)) {
        specialCharFound = 1;
      } else {
        //不符合要求的字符
        return false;
      }
    }

    return (alphabeticFound + digitalFound +  specialCharFound) > 1;
  }
}

代码测试

代码语言:javascript
复制
import java.util.Arrays;
import java.util.List;

import org.junit.Assert;
import org.junit.Test;

public class PasswordValidateExampleTest {

  // 不合规则的密码
  private static final List<String> INVALID_PWDS = Arrays.asList("123", "abc", "###", "123456", "abcABC", "123ab",
      "ab12%", "123456789012345678", "1234567890abcdefg", "1234567890abcd*#$");

  // 符合规则的密码
  private static final List<String> VALID_PWDS = Arrays.asList("123abc", "abc@$%", "12ab$a", "123456#%", "abcABC%",
      "1234567890abc@@", "1234567890abcAbc", "1234567890abc%*@");

  @Test
  public void testIsMatched1_invalid() {
    for (String invalidValue : INVALID_PWDS) {
      Assert.assertFalse(PasswordValidateExample.isMatched1(invalidValue));
    }
  }

  @Test
  public void testIsMatched1_valid() {
    for (String validValue : VALID_PWDS) {
      Assert.assertTrue(PasswordValidateExample.isMatched1(validValue));
    }
  }
}

使用junit跑一下,执行通过

至此,一个简单的非正则判断就写好了。接下来我们来看一下使用正则如何去完成。

2、正则表达式实现

准备

在给出最终正则表达式之前,我们先尝试给出只要符合一种以上字符即可的场景。

纯数字的正则表达式

代码语言:javascript
复制
^[0-9]*$ 或者^[\\d]*$

//限定长度6-16
^[0-9]{6,16}$ 或者^[\\d]{6,16}$

纯字母的正则表达式

代码语言:javascript
复制
^[A-Za-z]*$

//限定长度6-16
^[A-Za-z]{6,16}$

纯特殊字符的正则表达式

代码语言:javascript
复制
^[_*@!#%?$]*$

//限定长度6-16
^[_*@!#%?$]{6,16}$

数字、字母、特殊字符只要满足一种的

代码语言:javascript
复制
^[0-9A-Za-z_*@!#%?$]*$ 或者^[\\dA-Za-z_*@!#%?$]*$

//限定长度6-16
^[0-9A-Za-z_*@!#%?$]{6,16}$ 或者^[\\dA-Za-z_*@!#%?$]{6,16}$

思路

既然最终的目标至少2种组合,那么,我们写正则表达式只要将只有一种情况的情况排除即可。也就是:

代码语言:javascript
复制
1、排除纯数字的情况
2、排除纯字母的情况
3、排除纯特殊字符的情况

//使用排除的方法我们可以使用?!来完成

步骤

1、排除纯数字的情况

代码语言:javascript
复制
(?![0-9]+$) 或者(?![\\d]+$)

2、排除纯字母的情况

代码语言:javascript
复制
(?![A-Za-z]+$)

3、排除纯特殊字符的情况

代码语言:javascript
复制
(?![_*@!#%?$]+$)

4、合法字符

有了步骤#1、#2和#3的条件,我们已经把只有一种的情况排除在外,那么符合条件的字符肯定包含2种及其2种以上的情况。合法字符如下:

代码语言:javascript
复制
[0-9A-Za-z_*@!#%?$]{6,16} 或者 [\\dA-Za-z_*@!#%?$]{6,16}

5、最终成型

最后我们只要把上述正则连接起来,并加上开始^和结束$标记即可。

代码语言:javascript
复制
^(?![0-9]+$)(?![A-Za-z]+$)(?![_*@!#%?$]+$)[0-9A-Za-z_*@!#%?$]{6,16}$

或者

^(?![\\d]+$)(?![A-Za-z]+$)(?![_*@!#%?$]+$)[\\dA-Za-z_*@!#%?$]{6,16}$

程序实现

补充isMatched2和isMatched3方法。

代码语言:javascript
复制


import java.util.Arrays;
import java.util.List;

public class PasswordValidateExample {
  
  //特殊字符列表
  private static final List<Character> SPECIAL_CHARS = Arrays.asList('_','*','@','!','#','%','?','$');
  
  private static final String REGEX_1 = "^(?![0-9]+$)(?![A-Za-z]+$)(?![_*@!#%?$]+$)[0-9A-Za-z_*@!#%?$]{6,16}$";
  
  //与REGEX_1等价,只是用\\d来替换了0-9
  private static final String REGEX_2 = "^(?![\\d]+$)(?![A-Za-z]+$)(?![_*@!#%?$]+$)[\\dA-Za-z_*@!#%?$]{6,16}$";
  
  public static boolean isMatched2(String value) {
    return isMatched(value,REGEX_1 );
  }
  
  public static boolean isMatched3(String value) {
    return isMatched(value,REGEX_2 );
  }
  
  public static boolean isMatched(String value, String regex) {
    return value == null ? false :  value.matches(regex);
  }

  public static boolean isMatched1(String value) {
    if(value == null) {
      return false;
    }
    //检查长度
    int len = value.length();
    if(len <6 ||len >16) {
      return false;
    }
    int alphabeticFound = 0;
    int digitalFound = 0;
    int specialCharFound = 0;
    for(char c: value.toCharArray()) {
      if(Character.isAlphabetic(c)) {
        alphabeticFound = 1;
      }else if (Character.isDigit(c)) {
        digitalFound = 1;
      }else if(SPECIAL_CHARS.contains(c)) {
        specialCharFound = 1;
      } else {
        //不符合要求的字符
        return false;
      }
    }
    
    return (alphabeticFound + digitalFound +  specialCharFound) > 1;
  }
}

代码测试

补充isMatched2和isMatched3的测试方法。

代码语言:javascript
复制
import java.util.Arrays;
import java.util.List;

import org.junit.Assert;
import org.junit.Test;

public class PasswordValidateExampleTest {

  // 不合规则的密码
  private static final List<String> INVALID_PWDS = Arrays.asList("123", "abc", "###", "123456", "abcABC", "123ab",
      "ab12%", "123456789012345678", "1234567890abcdefg", "1234567890abcd*#$");

  // 符合规则的密码
  private static final List<String> VALID_PWDS = Arrays.asList("123abc", "abc@$%", "12ab$a", "123456#%", "abcABC%",
      "1234567890abc@@", "1234567890abcAbc", "1234567890abc%*@");

  @Test
  public void testIsMatched1_invalid() {
    for (String invalidValue : INVALID_PWDS) {
      Assert.assertFalse(PasswordValidateExample.isMatched1(invalidValue));
    }
  }

  @Test
  public void testIsMatched1_valid() {
    for (String validValue : VALID_PWDS) {
      Assert.assertTrue(PasswordValidateExample.isMatched1(validValue));
    }
  }
  
  @Test
  public void testIsMatched2_invalid() {
    for (String invalidValue : INVALID_PWDS) {
      Assert.assertFalse(PasswordValidateExample.isMatched2(invalidValue));
    }
  }

  @Test
  public void testIsMatched2_valid() {
    for (String validValue : VALID_PWDS) {
      Assert.assertTrue(PasswordValidateExample.isMatched2(validValue));
    }
  }
  
  @Test
  public void testIsMatched3_invalid() {
    for (String invalidValue : INVALID_PWDS) {
      Assert.assertFalse(PasswordValidateExample.isMatched3(invalidValue));
    }
  }

  @Test
  public void testIsMatched3_valid() {
    for (String validValue : VALID_PWDS) {
      Assert.assertTrue(PasswordValidateExample.isMatched3(validValue));
    }
  }
}

使用junit跑一下,执行通过

至此,开头提到的密码格式要求的判断就完成了。

密码格式要求:

  • 长度为6-16个字符
  • 字母(不分大小写)或数字或特殊字符(*、$、@、!、#、?)至少包含其中2种

3、套路回顾和扩展

套路步骤回顾

针对多种字符组合的判断,就是按照排除法的套路出牌,3个步骤即可。

代码语言:javascript
复制
先写出不符合的情况,拼接在一起
再写出合法字符的情况
最后加个开始^和结束$标记串联起来即可

1、排除纯数字的情况

代码语言:javascript
复制
(?![0-9]+$) 或者(?![\\d]+$)

2、排除纯字母的情况

代码语言:javascript
复制
(?![A-Za-z]+$)

3、排除纯特殊字符的情况

代码语言:javascript
复制
(?![_*@!#%?$]+$)

4、合法字符

有了步骤#1、#2和#3的条件,我们已经把只有一种的情况排除在外,那么符合条件的字符肯定包含2种及其2种以上的情况。合法字符如下:

代码语言:javascript
复制
[0-9A-Za-z_*@!#%?$]{6,16} 或者 [\\dA-Za-z_*@!#%?$]{6,16}

5、最终成型

最后我们只要把上述正则连接起来,并加上开始^和结束$标记即可。

代码语言:javascript
复制
^(?![0-9]+$)(?![A-Za-z]+$)(?![_*@!#%?$]+$)[0-9A-Za-z_*@!#%?$]{6,16}$

或者

^(?![\\d]+$)(?![A-Za-z]+$)(?![_*@!#%?$]+$)[\\dA-Za-z_*@!#%?$]{6,16}$

写法变形

针对多个(?!xxxx)(?!xxxx),我们也可以使用符号|连接做一个变形,如:

代码语言:javascript
复制
^(?!^([0-9]+|[A-Za-z]+|[_*@!#%?$]+)$)[0-9A-Za-z_*@!#%?$]{6,16}$

或者

^(?!^([\\d]+|[A-Za-z]+|[_*@!#%?$]+)$)[0-9A-Za-z_*@!#%?$]{6,16}$

同样,我们补充方法isMatched4isMatched5,并补充测试方法如下:

代码语言:javascript
复制
  //变形
  private static final String REGEX_3 = "^(?!^([0-9]+|[A-Za-z]+|[_*@!#%?$]+)$)[0-9A-Za-z_*@!#%?$]{6,16}$";
  
  private static final String REGEX_4 = "^(?!^([\\d]+|[A-Za-z]+|[_*@!#%?$]+)$)[0-9A-Za-z_*@!#%?$]{6,16}$";
  
  
  public static boolean isMatched4(String value) {
    return isMatched(value,REGEX_3 );
  }
  
  public static boolean isMatched5(String value) {
    return isMatched(value,REGEX_4 );
  }
  

测试一下

代码语言:javascript
复制
  @Test
  public void testIsMatched4_invalid() {
    for (String invalidValue : INVALID_PWDS) {
      Assert.assertFalse(PasswordValidateExample.isMatched4(invalidValue));
    }
  }

  @Test
  public void testIsMatched4_valid() {
    for (String validValue : VALID_PWDS) {
      Assert.assertTrue(PasswordValidateExample.isMatched4(validValue));
    }
  }
  
  @Test
  public void testIsMatched5_invalid() {
    for (String invalidValue : INVALID_PWDS) {
      Assert.assertFalse(PasswordValidateExample.isMatched5(invalidValue));
    }
  }

  @Test
  public void testIsMatched5_valid() {
    for (String validValue : VALID_PWDS) {
      Assert.assertTrue(PasswordValidateExample.isMatched5(validValue));
    }
  }

同样case执行成功

扩展

所以,针对上述的需求,我们的正则表达式可以写如下几种:

代码语言:javascript
复制
^(?![0-9]+$)(?![A-Za-z]+$)(?![_*@!#%?$]+$)[0-9A-Za-z_*@!#%?$]{6,16}$

或者

^(?![\\d]+$)(?![A-Za-z]+$)(?![_*@!#%?$]+$)[\\dA-Za-z_*@!#%?$]{6,16}$

或者
^(?!^([0-9]+|[A-Za-z]+|[_*@!#%?$]+)$)[0-9A-Za-z_*@!#%?$]{6,16}$

或者

^(?!^([\\d]+|[A-Za-z]+|[_*@!#%?$]+)$)[0-9A-Za-z_*@!#%?$]{6,16}$

通过前面的说明,想必大家对这种条件的正则已经有较好的理解了。

最后再来套用步骤练习一下,

密码格式要求调整下:

  • 长度为6-16个字符
  • 字母(不分大小写)或数字或特殊字符(*、$、@、!、#、?)3种都要包含

请先思考一下,再看答案

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

|

一个参考答案的步骤如下:

1、排除纯只包含数字字母的情况

代码语言:javascript
复制
(?![0-9A-Za-z]+$) 

2、排除纯只包含数字特殊字符的情况

代码语言:javascript
复制
(?![0-9_*@!#%?$]+$)

3、排除只包含字母特殊字符的情况

代码语言:javascript
复制
(?![A-Za-z_*@!#%?$]+$)

4、合法字符

有了步骤#1、#2和#3的条件,我们已经将如下几种情况排除

  • 只包含数字
  • 只包含字母
  • 只包含特殊字符
  • 只包含数字和字母
  • 只包含数字和特殊字符
  • 只包含字母和特殊字符

剩下只要写上合法字符情况,有了前面的排除条件,其必然都包含数字、字母和特殊字符。

代码语言:javascript
复制
[0-9A-Za-z_*@!#%?$]{6,16} 或者 [\\dA-Za-z_*@!#%?$]{6,16}

5、最终成型

最后我们只要把上述正则连接起来,并加上开始^和结束$标记即可。

代码语言:javascript
复制
^(?![0-9A-Za-z]+$)(?![0-9_*@!#%?$]+$)(?![A-Za-z_*@!#%?$]+$)[0-9A-Za-z_*@!#%?$]{6,16}$ 或者 [\\dA-Za-z_*@!#%?$]{6,16}

你学会了吗?

当然,还可以增加难度,比如支持中文;至少2种、3种或者全包含。有兴趣的读者可以按照上述套路尝试一下。有套路,不怕变形。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-09-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 孟君的编程札记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、非正则实现
  • 2、正则表达式实现
  • 3、套路回顾和扩展
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档