常见正则合法性校验

正则表达式用于各个情形的数据校验,用于匹配字符串。例如对请求参数的校验,客户端给服务器发送数据前,需初步对数据进行合法性校验;由于客户端数据不可信,服务器要对数据进行严格的合法性校验。
本文记录的是常用的身份证及手机号等合法性正则校验,以Java编程为例。

正则基本语法

^ 匹配字符串的起始位置,如果将 ^ 用作括号表达式中的第一个字符,则会对字符集求反,[^abc] 与除 a、b 和 c 以外的任何字符匹配

$ 匹配字符串的结束位置

* 任意次(零次或多次)匹配前面的字符或子表达式,等效于 {0,}

+ 至少一次(一次或多次)匹配前面的字符或子表达式,等效于 {1,}

? 零次或一次匹配前面的字符或子表达式,等效于 {0,1}

. 匹配单个字符, a.c 与“abc”、“a1c”和“a-c”匹配

| 或,z|food 与“z”或“food”匹配。(z|f)ood 与“zood”或“food”匹配

( ) 小括号表示匹配括号中全部字符

[] 中括号表示匹配括号中一个字符 范围描述 如[0-9 a-z A-Z]

{} 大括号用于限定匹配次数,如{n}表示匹配n次;{n,}表示至少匹配n次;{n,m}表示至少匹配n次,最多匹配m次

\ 转义字符,如上基本符号匹配都需要转义字符,如 * 表示匹配*号。\f 为换页符,\n 为换行符,\r 为回车符,\t 为Tab 字符,\v 为垂直制表符

\w 表示英文字母和数字以及下划线,等效于 [A-Za-z0-9_]

\W 英文字母,数字,下滑线以外的字符,等效于 [^A-Za-z0-9_]

\d 表示数字,等效于 [0-9]

\D 非数字,等效于 [^0-9]

\s 任何空白字符。其中包括空格、制表符和换页符,[\f\n\r\t\v]

\S 任何非空白字符,[^\f\n\r\t\v]

exp1(?=exp2) 前瞻(look ahead),查找exp2前面的exp1,如 a(?=b) 匹配ab但不匹配ac

(?<=exp2)exp1 后顾(look behind),查找exp2后面的exp1,如 (?<=b)a 匹配ba但不匹配bc

exp1(?!exp2) 负前瞻,查找后面不是exp2的exp1, 如 a(?!b) 匹配ac但不匹配ab

(?<!=exp2)exp1 负后顾,查找前面不是exp2的exp1, 如 (?<!b)a 匹配ca但不匹配ba

常见正则校验

手机号合法性校验

这里合法性校验对象为中国大陆及港澳手机号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 手机号合法性判断(大陆及港澳)
*/
public static boolean isPhoneLegal(String str) throws PatternSyntaxException {
return isMainLandPhoneLegal(str) || isHKPhoneLegal(str);
}
/**
* 大陆手机号码11位数,匹配格式:简单匹配
*/
public static boolean isChinaPhoneLegal(String str) throws PatternSyntaxException {
String regExp = "^1[3456789]\\d{9}$";
Pattern p = Pattern.compile(regExp);
Matcher m = p.matcher(str);
return m.matches();
}
/**
* 香港手机号码8位数,5|6|8|9开头+7位任意数
* 澳门手机号以6开头,可归到香港手机号判断中
*
*/
public static boolean isHKPhoneLegal(String str) throws PatternSyntaxException {
String regExp = "^(5|6|8|9)\\d{7}$";
Pattern p = Pattern.compile(regExp);
Matcher m = p.matcher(str);
return m.matches();
}

以上手机号校验太严格,一般情况,不建议使用,大陆手机号可用以下正则简单校验即可:

1
String regExp = "^1[34578]\\d{9}$";

身份证合法性校验

以下是完整的测试函数,包括15位和18位身份证号合法性校验以及15位到18位身份证号的转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* 身份证合法性校验及15位身份证转为18位
* <p>
* --15位身份证号码:第7、8位为出生年份(两位数),第9、10位为出生月份,第11、12位代表出生日期,第15位代表性别,奇数为男,偶数为女。
* --18位身份证号码:第7、8、9、10位为出生年份(四位数),第11、第12位为出生月份,第13、14位代表出生日期,第17位代表性别,奇数为男,偶数为女,最后一位为校验位。
*/
public class IdcardValidator {
/**
* 省、直辖市代码表:
* 11 : 北京 12 : 天津 13 : 河北 14 : 山西 15 : 内蒙古
* 21 : 辽宁 22 : 吉林 23 : 黑龙江 31 : 上海 32 : 江苏
* 33 : 浙江 34 : 安徽 35 : 福建 36 : 江西 37 : 山东
* 41 : 河南 42 : 湖北 43 : 湖南 44 : 广东 45 : 广西 46 : 海南
* 50 : 重庆 51 : 四川 52 : 贵州 53 : 云南 54 : 西藏
* 61 : 陕西 62 : 甘肃 63 : 青海 64 : 宁夏 65 : 新疆
* 71 : 台湾 81 : 香港 82 : 澳门 91 : 国外
*/
private static String cityCode[] = {"11", "12", "13", "14", "15", "21",
"22", "23", "31", "32", "33", "34", "35", "36", "37", "41", "42",
"43", "44", "45", "46", "50", "51", "52", "53", "54", "61", "62",
"63", "64", "65", "71", "81", "82", "91"};
/**
* 每位加权因子
*/
private static int power[] = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
/**
* 验证所有身份证的合法性
*
* @param idcard 身份证
* @return 合法返回true,否则返回false
*/
public static boolean isValidatedAllIdcard(String idcard) {
if (idcard == null) {
return false;
} else if (idcard.length() == 15) {
return validate15IDCard(idcard);
} else if (idcard.length() == 18) {
return validate18Idcard(idcard);
} else {
return false;
}
}
/**
* 判断18位身份证的合法性
* 根据〖中华人民共和国国家标准GB11643-1999〗中有关公民身份号码的规定,公民身份号码是特征组合码,由十七位数字本体码和一位数字校验码组成。
* 排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。
* 顺序码: 表示在同一地址码所标识的区域范围内,对同年、同月、同 日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配 给女性。
* <p>
* (1).前1、2位数字表示:所在省份的代码;
* (2).第3、4位数字表示:所在城市的代码;
* (3).第5、6位数字表示:所在区县的代码;
* (4).第7~14位数字表示:出生年、月、日;
* (5).第15、16位数字表示:所在地的派出所的代码;
* (6).第17位数字表示性别:奇数表示男性,偶数表示女性;
* (7).第18位数字是校检码:也有的说是个人信息码,一般是随计算机的随机产生,用来检验身份证的正确性。校检码可以是0~9的数字,有时也用x表示。
* <p>
* 第十八位数字(校验码)的计算方法为:
* (1).将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
* (2).将这17位数字和系数相乘的结果相加。
* (3).用加出来和除以11,看余数是多少
* (4).余数只可能有0 1 2 3 4 5 6 7 8 9 10这11个数字。其分别对应的最后一位身份证的号码为1 0 X 9 8 7 6 5 4 3 2
* (5).通过上面得知如果余数是2,就会在身份证的第18位数字上出现罗马数字的Ⅹ。如果余数是10,身份证的最后一位号码就是2。
*
* @param idcard 18位身份证号
* @return 是否合法
*/
public static boolean validate18Idcard(String idcard) {
if (idcard == null) {
return false;
}
// 非18位为假
if (idcard.length() != 18) {
return false;
}
// 获取前17位
String idcard17 = idcard.substring(0, 17);
// 前17位全部为数字
if (!isDigital(idcard17)) {
return false;
}
String provinceid = idcard.substring(0, 2);
// 校验省份
if (!checkProvinceid(provinceid)) {
return false;
}
// 校验出生日期
String birthday = idcard.substring(6, 14);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
try {
Date birthDate = sdf.parse(birthday);
String tmpDate = sdf.format(birthDate);
if (!tmpDate.equals(birthday)) {// 出生年月日不正确
return false;
}
} catch (ParseException e1) {
return false;
}
// 获取第18位
String idcard18Code = idcard.substring(17, 18);
char c[] = idcard17.toCharArray();
int bit[] = converCharToInt(c);
int sum17 = 0;
sum17 = getPowerSum(bit);
// 将和值与11取模得到余数进行校验码判断
String checkCode = getCheckCodeBySum(sum17);
if (null == checkCode) {
return false;
}
// 将身份证的第18位与算出来的校码进行匹配,不相等就为假
if (!idcard18Code.equalsIgnoreCase(checkCode)) {
return false;
}
return true;
}
/**
* 校验15位身份证
* 只校验省份和出生年月日
*
* @param idcard
* @return
*/
public static boolean validate15IDCard(String idcard) {
if (idcard == null) {
return false;
}
// 非15位为假
if (idcard.length() != 15) {
return false;
}
// 15全部为数字
if (!isDigital(idcard)) {
return false;
}
String provinceid = idcard.substring(0, 2);
// 校验省份
if (!checkProvinceid(provinceid)) {
return false;
}
String birthday = idcard.substring(6, 12);
SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");
try {
Date birthDate = sdf.parse(birthday);
String tmpDate = sdf.format(birthDate);
if (!tmpDate.equals(birthday)) {// 身份证日期错误
return false;
}
} catch (ParseException e1) {
return false;
}
return true;
}
/**
* 将15位的身份证转成18位身份证
*
* @param idcard
* @return
*/
public static String convertIdcarBy15bit(String idcard) {
if (idcard == null) {
return null;
}
// 非15位身份证
if (idcard.length() != 15) {
return null;
}
// 15全部为数字
if (!isDigital(idcard)) {
return null;
}
String provinceid = idcard.substring(0, 2);
// 校验省份
if (!checkProvinceid(provinceid)) {
return null;
}
String birthday = idcard.substring(6, 12);
SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");
Date birthdate = null;
try {
birthdate = sdf.parse(birthday);
String tmpDate = sdf.format(birthdate);
if (!tmpDate.equals(birthday)) {// 身份证日期错误
return null;
}
} catch (ParseException e1) {
return null;
}
Calendar cday = Calendar.getInstance();
cday.setTime(birthdate);
String year = String.valueOf(cday.get(Calendar.YEAR));
String idcard17 = idcard.substring(0, 6) + year + idcard.substring(8);
char c[] = idcard17.toCharArray();
String checkCode = "";
// 将字符数组转为整型数组
int bit[] = converCharToInt(c);
int sum17 = 0;
sum17 = getPowerSum(bit);
// 获取和值与11取模得到余数进行校验码
checkCode = getCheckCodeBySum(sum17);
// 获取不到校验位
if (null == checkCode) {
return null;
}
// 将前17位与第18位校验码拼接
idcard17 += checkCode;
return idcard17;
}
/**
* 校验省份
*
* @param provinceid
* @return 合法返回TRUE,否则返回FALSE
*/
private static boolean checkProvinceid(String provinceid) {
for (String id : cityCode) {
if (id.equals(provinceid)) {
return true;
}
}
return false;
}
/**
* 数字验证
*
* @param str
* @return
*/
private static boolean isDigital(String str) {
return str.matches("^[0-9]*$");
}
/**
* 将身份证的每位和对应位的加权因子相乘之后,再得到和值
*
* @param bit
* @return
*/
private static int getPowerSum(int[] bit) {
int sum = 0;
if (power.length != bit.length) {
return sum;
}
for (int i = 0; i < bit.length; i++) {
for (int j = 0; j < power.length; j++) {
if (i == j) {
sum = sum + bit[i] * power[j];
}
}
}
return sum;
}
/**
* 将和值与11取模得到余数进行校验码判断
*
* @param sum17
* @return 校验位
*/
private static String getCheckCodeBySum(int sum17) {
String checkCode = null;
switch (sum17 % 11) {
case 10:
checkCode = "2";
break;
case 9:
checkCode = "3";
break;
case 8:
checkCode = "4";
break;
case 7:
checkCode = "5";
break;
case 6:
checkCode = "6";
break;
case 5:
checkCode = "7";
break;
case 4:
checkCode = "8";
break;
case 3:
checkCode = "9";
break;
case 2:
checkCode = "x";
break;
case 1:
checkCode = "0";
break;
case 0:
checkCode = "1";
break;
}
return checkCode;
}
/**
* 将字符数组转为整型数组
*
* @param c
* @return
* @throws NumberFormatException
*/
private static int[] converCharToInt(char[] c) throws NumberFormatException {
int[] a = new int[c.length];
int k = 0;
for (char temp : c) {
a[k++] = Integer.parseInt(String.valueOf(temp));
}
return a;
}
public static void main(String[] args) throws Exception {
String idcard15 = "130321860311519";
String idcard18 = "210102198617083732";//
// 15位身份证
System.out.println(isValidatedAllIdcard(idcard15));
// 18位身份证
System.out.println(isValidatedAllIdcard(idcard18));
// 15位身份证转18位身份证
System.out.println(convertIdcarBy15bit(idcard15));
}
}

银行卡号合法性校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* 校验银行卡卡号
* @param cardId
* @return
*/
public static boolean checkBankCard(String cardId) {
if(cardId.length() != 16 && cardId.length() != 19){
return false;
}
char bit = getBankCardCheckCode(cardId.substring(0, cardId.length() - 1));
if(bit == 'N'){
return false;
}
return cardId.charAt(cardId.length() - 1) == bit;
}
/**
* 从不含校验位的银行卡卡号采用 Luhm 校验算法获得校验位
* @param nonCheckCodeCardId
* @return
*/
public static char getBankCardCheckCode(String nonCheckCodeCardId){
if(nonCheckCodeCardId == null || nonCheckCodeCardId.trim().length() == 0
|| !nonCheckCodeCardId.matches("\\d+")) {
//如果传的不是数据返回N
return 'N';
}
char[] chs = nonCheckCodeCardId.trim().toCharArray();
int luhmSum = 0;
for(int i = chs.length - 1, j = 0; i >= 0; i--, j++) {
int k = chs[i] - '0';
if(j % 2 == 0) {
k *= 2;
k = k / 10 + k % 10;
}
luhmSum += k;
}
return (luhmSum % 10 == 0) ? '0' : (char)((10 - luhmSum % 10) + '0');
}

密码合法性校验(6-10位字母数字组合为例)

1
2
3
4
5
6
public static boolean isNewPwdIllegal(String str) throws PatternSyntaxException {
String regExp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,10}$";
Pattern p = Pattern.compile(regExp);
Matcher m = p.matcher(str);
return m.matches();
}

时间格式yyyy-MM-dd hh:mm:ss

1
2
3
4
5
6
public static boolean isLegalTime(String str){
String regExp = "^(((([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]|[0-9][1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)) (20|21|22|23|[0-1][0-9]):[0-5][0-9]:[0-5][0-9])$";
Pattern p = Pattern.compile(regExp);
Matcher m = p.matcher(str);
return m.matches();
}

邮箱格式

1
2
3
4
5
6
public static boolean isMailLegal(String str){
String regExp = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$";
Pattern p = Pattern.compile(regExp);
Matcher m = p.matcher(str);
return m.matches();
}

Ipv4地址

1
2
3
4
5
6
public static boolean isIpv4(String str){
String regExp = "([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}";
Pattern p = Pattern.compile(regExp);
Matcher m = p.matcher(str);
return m.matches();
}

Boolean类型的字符串形式

1
2
3
4
5
6
public static boolean isBoolean(String str){
String regExp = "^(true)|(false)$";
Pattern p = Pattern.compile(regExp);
Matcher m = p.matcher(str.toLowerCase());
return m.matches();
}

unix时间戳,毫秒级,到2030年

1
2
3
4
5
6
public static boolean isLegalTimestamp(String str) {
String regExp = "^1[5-9]\\d{11}$";
Pattern p = Pattern.compile(regExp);
Matcher m = p.matcher(str);
return m.matches();
}

其他

1
2
3
4
5
合法身份证号简单检测:^\d{14}[\dxX]$|^\d{17}[\dxX]$
qq号:^[1-9][0-9]{4,10}$
微信号:^[a-zA-Z][-_a-zA-Z0-9]{5,19}$ 微信号规则参考:http://kf.qq.com/touch/faq/120813euEJVf141212Vfi6fA.html
英文和数字:^[A-Za-z0-9]+$
校验http或https开头,并且不包含反斜杠:^https?[^\\]*$