java实现身份证号码的严格校验!

题目

设计一个名字为IdCardCheck.java类,实现从键盘输入一个身份证号码字符串,判断输入的字符串是否符合身份证号码的规则,如果符合,计算输出年龄并输出它的年龄,输出你出生在那一年的第几周以及出生到现在已经经过了几周了。如果不符合,提示,并让其重新输入。直到输入正确身份证号码为止。(备注:作业题,描述不算很严谨,大体功能还是明确的)

分析

身份证号码基本格式的校验要用到【正则表达式】和java关于时间的API。题目功能主要是java关于时间的API的运用。

说明

本篇博客主要讲述如何实现:身份证号码的严格校验。讲述大体的实现思路和一些代码的具体实现细节,默认看博客的朋友已经基本了解正则表达式和java有关时间的API


身份证号码的结构分析

【地址码】

     第1、2位

【生日期码】

【顺序码】

【校验码】

思路

1.使用正则表达式校验基本格式

(1)地址码:前6位,其中第1位代表大区,我们可以明确知道第1位一定是1-6中的一个数字。后面的5位都是0-9的数字。

故地址码的正则表达式为:"^[1-6]\\d{5}"

当然,上面还罗列了所有的省份编号(身份证号码第1,2位),在后面可以通过打表去更严格地校验身份证号码的前2位

(2)生日期码

①年:4位,开头两位要么是18(这个可以不需要,因为现在基本上没有是19世纪出生的吧),要么是19,要么是20。后面两位是0-9的数字。正则表达式:"(18|19|20)\\d{2}"

②月:2位。分两种情况:①第1位为0,则第2位为数字0-9;②第1位为1,则第2位为0或1或2。正则表达式:"((0[1-9])|(1[0-2]))"

③日:2位。第1位可能是0,1,2,第2位可能为1-9的数字(为什么不是0-9,因为考虑错判00为正确的情况)。当然不要漏了特殊的几天:10,20,30,31。由于有些月没有31,且考虑2月的特殊性,在后面仍需进一步校验。正则表达式:"(([0-2][1-9])|10|20|30|31)"

(3)顺序码:3位。都是0-9的数字。正则表达式:"\\d{3}"

(4)校验码:校验码是根据前面17位计算出来的,这需要在后面进一步验证。它只能是0-9的数字或者X,x,这我们可以通过正则表达式验证:"[0-9X]"。(这里我不写成"[0-9Xx]"的原因见代码)

2.进一步验证

(1)我们知道身份证号码的第1,2位代表省份(自治区...),省份也就几十个,全部罗列在上面了,我们不妨打表去进一步校验。较简单,见代码

(2)生日期的校验

①身份证号码上面的生日期不能比当前时间还晚(比如说:现在是2019年,身份证号码的生日期是2030年,显然是不对的)

②上面的正则表达式会错判类似以下的情况,比如说:6月31日(事实上6月没有31号,但正则表达式错判为正确);平年2月29日(平年2月是没有29号的,但正则表达式也错判为正确)。这些情况也需要进一步排除,方法也很简单,也是打表,详情见代码

(3)计算校验码

根据前17位计算出相应的检验码(最后一位),如果用户输入的身份证号码的最后一位 与 根据前面17位计算出的校验码不一致,那么这个身份证号码就是错误的

代码

做这个作业(了解身份证号码结构,学习java关于时间的API,敲代码,总结写博客)花了我不少时间,总的来说收获还是蛮大的,写个博客做个笔记。

package theme3;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.Scanner;

/*
在lesson02中设计一个名字为IdCardCheck.java类,实现从键盘 输入一个身份证号码字符串,
判断输入的字符串是否符合身份证号码的规则,如果符合,计算输出年龄 输出它的年龄,输出你 
出生在那一年的第几周以及出生到现在已经经过了几周了。如果不符合,提示,并让其重新输入。
直到输入正确省份证号码为止
 */
public class IdCardCheck {
	public static void main(String[] args) throws ParseException {
		IdCardCheck ic=new IdCardCheck();
		ic.test();
		
	}
	//441225200002312559 //检验码正确,但2月份没有31天
	//441225201012092558 //生成的正确格式的身份证号码
	public void test() throws ParseException
	{
		Scanner sc=new Scanner(System.in);
		String idNum;	
		do
		{
			System.out.println("请输入身份证号码:");
			idNum=sc.nextLine();
		}while(!checkIdCardNum(idNum));
		
		System.out.println("身份证号码\""+idNum+"\"正确!");
		System.out.println("持卡人的年龄:"+getYear(idNum));
		System.out.println("出生的所在那一周是出生那年的第 "+getBirthWeek(idNum)+" 周");
		System.out.println("从出生到现在过去了 "+getWeeks(idNum)+" 周");
		
	}
	
	//身份证号码的严格校验
	public boolean checkIdCardNum(String idNum) throws ParseException
	{
		idNum=idNum.toUpperCase(); //将末尾可能存在的x转成X
		
		String regex="";
		regex+="^[1-6]\\d{5}"; //前6位地址码。后面仍需打表校验
		
		regex+="(18|19|20)\\d{2}"; //年份。后面仍需校验
		regex+="((0[1-9])|(1[0-2]))"; //月份。后面仍需校验
		regex+="(([0-2][1-9])|10|20|30|31)"; //日期。后面仍需校验
		
		regex+="\\d{3}"; //3位顺序码
		
		regex+="[0-9X]"; //检验码。后面仍需验证
		
		if(!idNum.matches(regex))
			return false;
		
		//第1,2位(省)打表进一步校验
		int[] d={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,53,
				61,62,63,64,65,
				83,81,82};
		boolean flag=false;
		int prov=Integer.parseInt(idNum.substring(0, 2));
		for(int i=0;i<d.length;i++)
			if(d[i]==prov)
			{
				flag=true;
				break;
			}
		if(!flag)	
			return false;
		
		//生日校验:生日的时间不能比当前时间(指程序检测用户输入的身份证号码的时候)晚
		SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMdd"); 
		Date birthDate=sdf.parse(idNum.substring(6, 14));
		Date curDate=new Date();
		if(birthDate.getTime()>curDate.getTime())
			return false;
		
		//生日校验:每个月的天数不一样(有的月份没有31),还要注意闰年的二月
		int year=Integer.parseInt(idNum.substring(6, 10));
		int leap=((year%4==0 && year%100!=0) || year%400==0)?1:0;
		final int[] month={0,31,28+leap,31,30,31,30,31,31,30,31,30,31};
		int mon=Integer.parseInt(idNum.substring(10, 12));
		int day=Integer.parseInt(idNum.substring(12, 14));
		if(day>month[mon])
		{
			//System.out.println(day+" "+month[mon]+"\n");
			//System.out.println("---");
			return false;
		}
			
		
		//检验码
		if(idNum.charAt(17)!=getLastChar(idNum))
			return false;
			
		return true;
	}
	
	//根据身份证号码的前17位计算校验码
	public char getLastChar(String idNum) //由于这个功能比较独立,就分离出来
	{
		final int[] w={0,7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};
		final char[] ch={'1','0','X','9','8','7','6','5','4','3','2'}; //这就是为什么一开始将末尾可能存在的x转成X的原因
		int res=0;
		for(int i=0;i<17;i++)
		{
			int t=idNum.charAt(i)-'0';
			res+=(t*w[i+1]);
		}
		return ch[res%11];
	}
	
	//根据身份证号码的出生日期计算年龄
	public int getYear(String idNum)
	{
		int yearBirth=Integer.parseInt(idNum.substring(6, 10));
		int monBirth=Integer.parseInt(idNum.substring(10, 12));
		int dayBirth=Integer.parseInt(idNum.substring(12, 14));
		
		Calendar cur=Calendar.getInstance();
		int yearCur=cur.get(Calendar.YEAR);
		int monCur=cur.get(Calendar.MONTH)+1; //不要忘了+1
		int dayCur=cur.get(Calendar.DATE);
		
		
		//System.out.println(yearCur+" "+monCur+" "+dayCur);
		int age=yearCur-yearBirth;
		if(monCur<monBirth || (monCur==monBirth && dayCur<dayBirth))
			age--;
		
		return age;
	}
	
	//出生的所在那一周是出生那年的第几周
	public int getBirthWeek(String idNum) throws ParseException
	{		
		SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMdd"); 
		Date birthDate=sdf.parse(idNum.substring(6, 14));  //默认0时0分0秒
		Calendar calendar=Calendar.getInstance();
		calendar.setTime(birthDate); 
		return calendar.get(Calendar.WEEK_OF_YEAR);
	}
	
	//从出生到现在过去了多少周
	public int getWeeks(String idNum) throws ParseException
	{	
		SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMdd"); 
		Date birthDate=sdf.parse(idNum.substring(6, 14));  //默认0时0分0秒
		Date curDate=new Date();
		return  (int)( (curDate.getTime()-birthDate.getTime())/(long)(7*24*60*60*1000) ) ; //不建议把分子转成int,可能会溢出
	}
	
}

 

  • 18
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值