Java身份证验证CardID类

=====taoyyz小陶©版权所有=====

实验三 身份证号码验证及个人信息输出。

要求:

输入身份证号码,进行三重验证,并且输出对应信息。
1、身份证验证主要包含:
  1. 位数错误:正确应该为 18 位。
  2. 字符错误:前面 17 个应该是数字,最后一位可以为‘X’或‘x’;
  3. 校验码错误:
    身份证的最后一位为校验码,校验码是用于验证前面的数字是否有错误,校
验码运算规则:
    (1)先将最后一位设定为 0。
    (2)效验码的计算公式为:(Σ (ai×wi))%11
  其中:i 是从右向左的包括效验码在内的序号(1-18)
  ai 是第 i 位的数字
  wi 是第 i 位上的加权因子,其数值依据公式 wi=(2(i-1))%11,(计算 ab:函数为:java.lang.Math.pow(a,b)) i,ai,wi 的对应关系如下:

1
2
3
身份证号码: 5  1  0  1  0  1  1  9  8  8 0 8 0 8 0 1 2 0
i :18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
wi : 用公式计算 2^(i-1)%11 …… 9 10 5 8 4 2 1

    (3)求出(Σ (ai×wi))%11 之后依据下表得到效验码:

1
2
(Σ (ai×wi))%11 :0 1 2 3 4 5 6 7 8 9 10
校验码值:1 0 X 9 8 7 6 5 4 3 2

    (4)计算 ab:函数为:java.lang.Math.pow(a,b)
2、个人信息输出主要包含:
  1: 生日:如 1985 年 5 月 9 日。
  2: 年龄:如 20 岁,应当精确到日;
  3: 性别:顺序码的计数为,男性以奇数计数,女性以偶数计数
    (1)编写一个身份证类,包含一个属性:身份证号;三个验证函数;一个信息输出函数。
    (2)编写一个测试类,可从键盘上输入一个身份证号码,产生一个身份证对象,然后调用其相关方法对身份证号码进行验证,没有通过验证的话,提示错
误信息并让用户重新输入,如果通过验证,则输出该人的信息。
代码框架如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class CardID{
private String cardNum;
public void setCardNum(String cardNum){//换一个号码
this. cardNum= cardNum; }
public boolean lengthVerify(){○1 位数错误验证
}
public boolean charVerify(){○2 字符错误验证
}
public boolean checkcodeVerify(){○3 校验码错误验证
//测试时请将每一轮的 i,ai,wi,sum 输出,避免出错,成功后不再输出
}
public void output(){○4 信息输出
//按要求输出个人信息
}
}
class CardIDDemo{
public static void main(String []args){
//用死循环反复测试输入、验证、输出
//注意第一步验证失败不能进入第二步,第二步失败不能进入第三步,所有验
证通过才能进行输出。
}
}

思路:

需要两个类:Card类和CardID类
其中:1.在Card类中定义String类型的cardNum变量
  2.在Card类中定义Getter/Setter方法
  3.在Card类中定义3种验证方法:
    3.1对于位数验证,直接判断输入的cardNum长度是否为18
    3.2对于字符验证,前17位为数字,最后一位还可以为大小写的’x’
    3.3对于校验码,需要取出每一位,转化为整形进行算术运算
  4.在output()输出方法中,输出生日、年龄、性别
  5.以上3种输出需求均在身份证号码中截取获得
  6.利用Calendar类不是必须的,但是逻辑清晰,有利于日期计算
  7.年龄判断不是简单的相减,需要考虑到月份和天数不够减情况下的处理

代码:

CardID类:
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
package com.experiment.exp3;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Objects;

/**
* @Author taoyyz(陶俊杰)
* @Date 2021/3/3 15:25
* @Version 1.0
*/
public class CardID {
private String cardNum;

public void setCardNum(String cardNum) {
this.cardNum = cardNum;
}

//位数错误验证 (是否为18位)
public boolean lengthVerify() {
if (this.cardNum.length() == 18) {
return true;
} else {
System.out.print("位数验证错误!");
return false;
}
}

//字符错误验证(前17位为数字,最后一位为'X'或'x')
public boolean charVerify() {
char[] chars = this.cardNum.toCharArray();
for (int i = 0; i < chars.length - 1; i++) {
if (!('0' <= chars[i] && chars[i] <= '9')) {
System.out.print("字符验证错误!");
return false;
}
}
if (('0' <= chars[17] && chars[17] <= '9') || chars[17] == 'x' || chars[17] == 'X') {
return true;
} else {
System.out.print("字符验证错误!");
return false;
}
}

//校验码错误验证 (最后一位)
public boolean checkCodeVerify() {
//先把输入的字符串类型身份证号转为字符串数组
String[] cardNumStr = numStringToArray();
//定义整型数组ai,以存放身份证号每一位的整型值,方便算术运算
int[] ai = new int[18];
//先把最后一位设定为0
ai[ai.length - 1] = 0;
//把身份证号除最后一位外,依次存入整型数组ai中(因为最后一位可能为字符'x'或'X')
for (int i = 0; i < cardNumStr.length - 1; i++) {
ai[i] = Integer.parseInt(cardNumStr[i]);
}
int[] wi = new int[18];
//计算wi[]数组每一位
for (int i = ai.length; i > 0; i--) {
wi[18 - i] = (int) Math.pow(2, (i - 1)) % 11;
}
//计算校验码 verifyCode = (∑(ai x wi))%11
int result = 0;
for (int i = 0; i < ai.length; i++) {
result += ai[i] * wi[i];
}
int verifyCode = result % 11;
//定义一个字符串数组,存储每一位校验码应该对应的校验码值
String[] verifyCodeList = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};
//判断校验码是否正确
String lastChar = verifyCodeList[verifyCode];
System.out.println("身份证最后一位应该是" + "'" + lastChar + "'");
//注意在判断时,校验码数组只有大写的'X',用户输入的可能为小写,会导致误判。应该都转为小写/大写再判断
if (Objects.equals(cardNumStr[cardNumStr.length - 1].toLowerCase(), lastChar.toLowerCase())) {
return true;
} else {
System.out.println("校验码验证错误!");
return false;
}
}

//信息输出
public void output() {
/*
生日 第7位到第10位年 第11位到第12位月 第13位到第14位日 yyyy年M月d日
年龄 今天 - 生日 精确到日
性别 第15到17位 奇数男 偶数女
*/
/*调用相应的方法计算生日、年龄、性别*/
// 生日
//格式化Calendar类的生日为指定格式
Calendar birthDate = calcBirth();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年M月d日"); //格式化时间成字符串
String birth = sdf.format(birthDate.getTime());
// 年龄
String age = calcAge(birthDate);
// 性别
String sex = calcSex();

//输出计算出的生日、年龄、性别
System.out.println("生日:" + birth);
System.out.println("年龄:" + age);
System.out.println("性别:" + sex);
System.out.println("===========");
}

//计算性别
private String calcSex() {

int numOfSex = Integer.parseInt(this.cardNum.substring(14, 17));
return numOfSex % 2 != 0 ? "男" : "女";
}

//计算年龄
private String calcAge(Calendar birthDateString) {
//解析生日到单独的变量中
int yearOfBirth = birthDateString.get(Calendar.YEAR);
int monthOfBirth = birthDateString.get(Calendar.MONTH) + 1;
int dayOfBirth = birthDateString.get(Calendar.DATE);
//获取今天的日期到单独变量中
Calendar nowDate = Calendar.getInstance();
int yearOfNow = nowDate.get(Calendar.YEAR);
int monthOfNow = nowDate.get(Calendar.MONTH) + 1;
int dayOfNow = nowDate.get(Calendar.DATE);
//计算年份差
int ageOfYear = yearOfNow - yearOfBirth;
//计算月份差
int ageOfMonth = monthOfNow - monthOfBirth;
if (ageOfMonth < 0) { //今年的月份比出生月份小
ageOfYear--; //年龄 - 1
ageOfMonth += 12; //月份补12个月
}
//计算天数差
int ageOfDay = dayOfNow - dayOfBirth;
if (ageOfDay < 0) { //天数比生日天数小
ageOfMonth--; //月份 - 1
ageOfDay += nowDate.getActualMaximum(Calendar.DAY_OF_MONTH) + 1; //天数补齐一个月
/*这里有个小争议:如果活了某个月的最大天数,也可以算作活了这个月的下个月的第0天
对于某人的生日来说,+1 操作相当于把今天看做为新的一岁的第0天,而不是活了11月30天(因为活了12个月相当于就是下一年了)
加不加1就看怎么理解今天作为生日的话,到底是把今天算作这一岁的最后一天还是下一岁新的一天*/
}
//再次修正月份以及年份
if (ageOfMonth < 0) { //上一步计算天数时,可能导致原本0月自减1变成-1
ageOfYear--; //修正岁数 - 1
ageOfMonth += 12; //月份补12个月
}
return ageOfYear + "岁" + ageOfMonth + "月" + ageOfDay + "天";
}

//计算生日
private Calendar calcBirth() {
//从输入的身份证号获取相应位置的字符串,转换成整数型
int year = Integer.parseInt(this.cardNum.substring(6, 10)); //注意substring()下标[begin,end)
int month = Integer.parseInt(this.cardNum.substring(10, 12));
int day = Integer.parseInt(this.cardNum.substring(12, 14));
Calendar birthDate = Calendar.getInstance();
//把Calendar类的birthDate设置为获取到的整数型的年、月、日
birthDate.set(Calendar.YEAR, year);
birthDate.set(Calendar.MONTH, month - 1); //Calendar类月份从0到11月,实际上的8月只是MONTH的7月
birthDate.set(Calendar.DATE, day);
//把计算出来的birthDate作为Calendar类返回值带回
return birthDate;
}

//把输入的身份证字符串转化为字符串数组
private String[] numStringToArray() {
char[] chars = this.cardNum.toCharArray();
String[] numArray = new String[cardNum.length()];
for (int i = 0; i < cardNum.length(); i++) {
numArray[i] = String.valueOf(chars[i]); //身份证号每一位的字符顺序存入int数组
}
return numArray;
}
}

CardIDDemo类(含main方法):
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
package com.experiment.exp3;
/*
(1)编写一个身份证类,包含一个属性:身份证号;三个验证函数;一个
信息输出函数。
(2)编写一个测试类,可从键盘上输入一个身份证号码,产生一个身份证
对象,然后调用其相关方法对身份证号码进行验证,没有通过验证的话,提示错
误信息并让用户重新输入,如果通过验证,则输出该人的信息。
1。用死循环反复测试输入、验证、输出
2。注意第一步验证失败不能进入第二步,第二步失败不能进入第三步,所有验证通过才能进行输出。
*/

import java.util.Scanner;

/**
* @Author taoyyz(陶俊杰)
* @Date 2021/3/3 15:31
* @Version 1.0
*/
public class CardIDDemo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入一个身份证号:");
String cardId = sc.next(); //键盘输入一个身份证号码
CardID cardID = new CardID(); //产生一个身份证对象
//开始验证
cardID.setCardNum(cardId);
if (cardID.lengthVerify()) { //位数错误验证
if (cardID.charVerify()) { //字符错误验证
if (cardID.checkCodeVerify()) { //校验码错误验证
System.out.println("验证通过");
cardID.output();
} else {
error();
}
} else {
error();
}
} else {
error();
}
}
}

public static void error() {
System.out.println("验证错误,请重新输入");
System.out.println("-----------");
}
}

运行截图:

CardID01 CardID02

心得:

  这个CardID类考验面向对象的综合能力,特别是成员方法,如何进行三重验证。其次就是多次用到字符串截取:String类的substring()方法
  在年龄计算那一块,精确到年很简单,属于有手就行。精确到月也只是多一步判断,就是当前月份-出生月份为负数时,说明当前月份比出生月份小,那么年龄就要-1,然后修正月份,也就是月份+=12。
  天数的处理跟上一步类似。要注意天数相减为负数时导致月份-1,这时候还需要再次判断月份是否为负。否则会导致原本0月,经过天数不够减之后,月份成为负1月。

感谢阅读💗