在平时开发时,经常会有格式要求的判断,比如密码格式要求:
面对这样的一个判断要求,我们该如何实现呢?本文给出不使用正则和使用正则表达式两种解决方法,其中掌握正则的套路步骤可以较好的理解,去应用不同的场景。
思路
1、判断字符串是否为空
2、判断长度
3、定义三个变量,判断对应的字符是否找到
int alphabeticFound = 0;
int digitalFound = 0;
int specialCharFound = 0;
依次判断字符是否满足字母、数字或者特殊字符。如满足符合要求的字符,则相关变量赋值为1;如不符合则直接返回false,表示出现不符合要求的字符。
4、判断三个变量找到的情况,累加大于1,则表示至少满足2种字符
代码实现
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;
}
}
代码测试
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跑一下,执行通过
至此,一个简单的非正则判断就写好了。接下来我们来看一下使用正则如何去完成。
准备
在给出最终正则表达式之前,我们先尝试给出只要符合一种以上字符即可的场景。
纯数字的正则表达式
^[0-9]*$ 或者^[\\d]*$
//限定长度6-16
^[0-9]{6,16}$ 或者^[\\d]{6,16}$
纯字母的正则表达式
^[A-Za-z]*$
//限定长度6-16
^[A-Za-z]{6,16}$
纯特殊字符的正则表达式
^[_*@!#%?$]*$
//限定长度6-16
^[_*@!#%?$]{6,16}$
数字、字母、特殊字符只要满足一种的
^[0-9A-Za-z_*@!#%?$]*$ 或者^[\\dA-Za-z_*@!#%?$]*$
//限定长度6-16
^[0-9A-Za-z_*@!#%?$]{6,16}$ 或者^[\\dA-Za-z_*@!#%?$]{6,16}$
思路
既然最终的目标至少2种组合,那么,我们写正则表达式只要将只有一种情况的情况排除即可。也就是:
1、排除纯数字的情况
2、排除纯字母的情况
3、排除纯特殊字符的情况
//使用排除的方法我们可以使用?!来完成
步骤
1、排除纯数字的情况
(?![0-9]+$) 或者(?![\\d]+$)
2、排除纯字母的情况
(?![A-Za-z]+$)
3、排除纯特殊字符的情况
(?![_*@!#%?$]+$)
4、合法字符
有了步骤#1、#2和#3的条件,我们已经把只有一种的情况排除在外,那么符合条件的字符肯定包含2种及其2种以上的情况。合法字符如下:
[0-9A-Za-z_*@!#%?$]{6,16} 或者 [\\dA-Za-z_*@!#%?$]{6,16}
5、最终成型
最后我们只要把上述正则连接起来,并加上开始^和结束$标记即可。
^(?![0-9]+$)(?![A-Za-z]+$)(?![_*@!#%?$]+$)[0-9A-Za-z_*@!#%?$]{6,16}$
或者
^(?![\\d]+$)(?![A-Za-z]+$)(?![_*@!#%?$]+$)[\\dA-Za-z_*@!#%?$]{6,16}$
程序实现
补充isMatched2和isMatched3方法。
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的测试方法。
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跑一下,执行通过
至此,开头提到的密码格式要求的判断就完成了。
密码格式要求:
套路步骤回顾
针对多种字符组合的判断,就是按照排除法的套路出牌,3个步骤即可。
先写出不符合的情况,拼接在一起
再写出合法字符的情况
最后加个开始^和结束$标记串联起来即可
1、排除纯数字的情况
(?![0-9]+$) 或者(?![\\d]+$)
2、排除纯字母的情况
(?![A-Za-z]+$)
3、排除纯特殊字符的情况
(?![_*@!#%?$]+$)
4、合法字符
有了步骤#1、#2和#3的条件,我们已经把只有一种的情况排除在外,那么符合条件的字符肯定包含2种及其2种以上的情况。合法字符如下:
[0-9A-Za-z_*@!#%?$]{6,16} 或者 [\\dA-Za-z_*@!#%?$]{6,16}
5、最终成型
最后我们只要把上述正则连接起来,并加上开始^和结束$标记即可。
^(?![0-9]+$)(?![A-Za-z]+$)(?![_*@!#%?$]+$)[0-9A-Za-z_*@!#%?$]{6,16}$
或者
^(?![\\d]+$)(?![A-Za-z]+$)(?![_*@!#%?$]+$)[\\dA-Za-z_*@!#%?$]{6,16}$
写法变形
针对多个(?!xxxx)(?!xxxx),我们也可以使用符号|连接做一个变形,如:
^(?!^([0-9]+|[A-Za-z]+|[_*@!#%?$]+)$)[0-9A-Za-z_*@!#%?$]{6,16}$
或者
^(?!^([\\d]+|[A-Za-z]+|[_*@!#%?$]+)$)[0-9A-Za-z_*@!#%?$]{6,16}$
同样,我们补充方法isMatched4和isMatched5,并补充测试方法如下:
//变形
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 );
}
测试一下
@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执行成功
扩展
所以,针对上述的需求,我们的正则表达式可以写如下几种:
^(?![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}$
通过前面的说明,想必大家对这种条件的正则已经有较好的理解了。
最后再来套用步骤练习一下,
密码格式要求调整下:
请先思考一下,再看答案
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
一个参考答案的步骤如下:
1、排除纯只包含数字字母的情况
(?![0-9A-Za-z]+$)
2、排除纯只包含数字特殊字符的情况
(?![0-9_*@!#%?$]+$)
3、排除只包含字母特殊字符的情况
(?![A-Za-z_*@!#%?$]+$)
4、合法字符
有了步骤#1、#2和#3的条件,我们已经将如下几种情况排除
剩下只要写上合法字符情况,有了前面的排除条件,其必然都包含数字、字母和特殊字符。
[0-9A-Za-z_*@!#%?$]{6,16} 或者 [\\dA-Za-z_*@!#%?$]{6,16}
5、最终成型
最后我们只要把上述正则连接起来,并加上开始^和结束$标记即可。
^(?![0-9A-Za-z]+$)(?![0-9_*@!#%?$]+$)(?![A-Za-z_*@!#%?$]+$)[0-9A-Za-z_*@!#%?$]{6,16}$ 或者 [\\dA-Za-z_*@!#%?$]{6,16}
你学会了吗?
当然,还可以增加难度,比如支持中文;至少2种、3种或者全包含。有兴趣的读者可以按照上述套路尝试一下。有套路,不怕变形。