Java开发笔记(三十八)利用正则表达式校验字符串

前面多次提到了正则串、正则表达式,那么正则表达式究竟是符合什么定义的字符串呢?正则表达式是编程语言处理字符串格式的一种逻辑式子,它利用若干保留字符定义了形形色色的匹配规则,从而通过一个式子来覆盖满足了上述规则的所有字符串。正则表达式的保留字符主要有:圆括号、方括号、花括号、竖线、横线、点号、加号、星号、反斜杆等等,这些保留字符的作用详见前一篇博文,下面再简单总结一下它们的用途:
圆括号“()”:把圆括号内外的表达式区别开来。
方括号“[]”:表示方括号内部的字符互相之间是或的关系。
花括号“{}”:花括号中间填写数字,表示花括号前面的字符有多少位。
竖线“|”:对前面和后面的字符进行或运算,表示既可以是前面的字符,也可以是后面的字符。
横线“-”:与前面和后面的字符组合起来,代表两个字符之间的所有连续字符。
点号“.”:代表除了回车符和换行符以外的其它字符。
加号“+”:表示加号前面的字符可以有一位,也可以有多位。
星号“*”:表示星号前面的字符可以有一位,也可以有多位,还可以没有(0位)。
反斜杆“\”:两个反斜杆可对保留字符进行转义,表示保留字符的自身符号。
正则表达式除了用在split方法中切割字符串,还可以用在matches方法中判断字符串是否符合正则条件。以手机号码为例,不管是移动还是联通还是电信的手机号,统统都是11位数字,并且第一位数字固定为1,第二位数字可能是3、4、5、7、8,再加上9位数字凑成11位手机号。那么通过正则表达式书写11位手机号码的规则,第一位就用“1”表示,第二位可用“[34578]”表示,后面的9位数字使用“\\d{9}”表达,整合起来便形成了最终的手机号码正则串“1[34578]\\d{9}”。下面的isPhone方法,就是根据这个正则表达式校验手机号码的代码例子:

	// 利用正则表达式检查字符串是否为合法的手机号码
	public static boolean isPhone(String phone) {
		// 开头的"1"代表第一位为数字1,"[34578]"代表第二位可以为3、4、5、7、8其中之一,"\\d{9}"代表后面是9位数字
		String regex = "1[34578]\\d{9}";
		// 字符串变量的matches方法返回正则表达式对该串的检验结果,true表示符合字符串规则,false表示不符合规则
		return phone.matches(regex);
	}

 

再来一个更复杂的字符串校验——身份证号码的格式校验,中国的二代身份证号码共有18位,其中前六位是地区编码,中间八位是公民的出生年月日,后面三位是该地区当日的出生序号,最后一位是校验码。国家把各省区划分为七大块,地区编码的首位为1代表华北地区,为2代表东北地区,为3代表华东地区,为4代表中南地区,为5代表西南地区,为6代表西北地区,为8代表港澳台特别行政区。地区编码的第二位代表大区域下面的具体省区,再后面的位数表示下面的地市乃至县区,通常只要校验地区编码的前两位就行了,于是得到如下的地区校验的正则方法代码例子:

	// 校验身份证号码开头的六位地区编码
	public static void checkArea() {
		String regex = "(1[1-5]|2[1-3]|3[1-7]|4[1-6]|5[0-4]|6[1-5]|8[1-3])\\d{4}";
		for (int i=0; i<=90; i++) {
			String area = String.format("%06d", i*10000);
			boolean check = area.matches(regex);
			System.out.println("area = "+area+", check = "+check);
		}
	}

 

身份证号码中间的八位出生年月日,可再拆分为四位的年份、两位的月份和两位的日期。一个健在公民的出生年份,只可能是二十世纪和二十一世纪的某一年,也就是说,四位年份必定以19或者20开头,因此正则串“(19|20)\\d{2}”即可覆盖这两个世纪的两百个年份。此时校验年份的正则方法代码如下所示:

	// 校验四位的年份字符串
	public static void checkYear() {
		String regex = "(19|20)\\d{2}";
		for (int i=1899; i<=2100; i++) {
			if (i>1910 && i<2090) {
				continue;
			}
			String year = i+"";
			boolean check = year.matches(regex);
			System.out.println("year = "+year+", check = "+check);
		}
	}

 

年份校验完毕,后面的月份更简单,因为两位月份就是“01”到“12”中间的十二个数字。如果月份首位是0,那么第二位可以是1到9;如果月份首位是1,那么第二位可以是0到2。据此可把月份的正则表达式分解成两个关系为“或”的子表达式,其中第一个表达式可使用“0[1-9]”,第二个表达式可使用“1[0-2]”,两个表达式通过竖线连接起来便形成了完整的月份表达式“0[1-9]|1[0-2]”。此时校验月份的正则方法代码如下所示:

	// 校验两位的月份字符串
	public static void checkMonth() {
		String regex = "0[1-9]|1[0-2]";
		for (int i=0; i<=13; i++) {
			String month = String.format("%02d", i);
			boolean check = month.matches(regex);
			System.out.println("month = "+month+", check = "+check);
		}
	}

 

月份后面的日期,校验起来稍微有些复杂。合法的两位日期可以是“01”到“31”中间的三十一个数字,故而日期的正则校验需要分解成以下的三种情况:
1、日期首位是0,那么第二位可以是1到9,该情况的正则表达式应为“0[1-9]”。
2、日期首位是1或者2,那么第二位可以是0到9,该情况的正则表达式应为“[12]\\d”。
3、日期首位是3,那么第二位可以是0和1,该情况的正则表达式应为“3[01]”。
综合以上的三种情况,得到完整的日期校验正则串为“0[1-9]|[12]\\d|3[01]”。此时校验日期的正则方法代码如下所示:

	// 校验两位的日期字符串
	public static void checkDay() {
		String regex = "0[1-9]|[12]\\d|3[01]";
		for (int i=0; i<=32; i++) {
			String day = String.format("%02d", i);
			boolean check = day.matches(regex);
			System.out.println("day = "+day+", check = "+check);
		}
	}

 

然后还要校验身份证号码的末尾四位,包括三位的出生编码和一位的校验码。其中出生编码为三位数字,而校验码除了数字以外还可能是小写的x或者大写的X,因此出生编码和校验码也得分别加以判断。三位的出生编码,对应的正则表达式为“\\d{3}”;一位的校验码,对应的正则表达式为“[0-9xX]”;二者的式子合起来,就变成了“\\d{3}([0-9xX])”。下面的方法代码可生成四位的字符串,并进行身份证末四位的正则校验:

	// 校验身份证号码末尾的四位编号串
	public static void checkLastFour() {
		String regex = "\\d{3}([0-9xX])";
		for (int i=0; i<36; i++) {
			char last;
			if (i < 10) {
				// 转换成数字字符
				last = (char) ('0'+i);
			} else {
				// 转换成字母字符
				last = (char) ('A' + i-10);
			}
			String lastFour = String.format("%03d%c", i*13, last);
			boolean check = lastFour.matches(regex);
			System.out.println("lastFour = "+lastFour+", check = "+check);
		}
	}

 

以上把18位身份证号码的各个区间分别做了正则校验,最后还要组装各区间的正则表达式。这时为了避免各区间的表达式互相干扰,可以利用圆括号将各区间的作用范围先行界定,就像下面这样“(六位地区编码)(四位年份)(两位月份)(两位日期)(末尾四位编号)”,接着再把各区间的正则表达式分别填入该区间的圆括号之中,便形成了最终的身份证号码正则串。包含正则串在内的身份证校验的完整方法如下所示:

	// 利用正则表达式检查字符串是否为合法的身份证号码
	public static boolean isICNO(String icno) {
		//String regex = "(六位地区编码)(四位年份)(两位月份)(两位日期)(末尾四位编号)";
		String regex = "((1[1-5]|2[1-3]|3[1-7]|4[1-6]|5[0-4]|6[1-5]|8[1-3])\\d{4})((19|20)\\d{2})(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])(\\d{3}([0-9xX]))";
		return icno.matches(regex);
	}

  

更多Java技术文章参见《Java开发笔记(序)章节目录

posted @ 2018-12-17 22:11  pinlantu  阅读(6919)  评论(0编辑  收藏  举报