本文主要讨论的是二代身份证编码规则及其Java代码实现,下面的校验方式还不是特别严谨,由于只校验了前两位的省份信息,中间六位的出生日期信息和最后一位的校验码信息,故对于部分不满足要求的证件号码刚好同时满足了这里提到的几个条件,也会被判定为是合法的证件号码…
1 二代身份证号码编码规则
1.1 编码格式
1999年我国颁发了第二代居民身份证号,公民身份号码为18位,且终身不变。
居民身份证格式如:ABCDEFYYYYMMDDXXXR
1.1.1地址码(ABCDEF)
表示登记户口时所在地的行政区划代码(省、市、县),如果行政区划进行了重新划分,同一个地方进行户口登记的可能存在地址码不一致的情况。行政区划代码按GB/T2260的规定执行。
1.1.2 出生日期码(YYYYMMDD)
表示该居民的出生年月日,年4位数字,月和日分别用2位数字表示,如19491001,;出生日期码是按GB/T 7408的规定执行的。
1.1.3 顺序码(XXX)
表示同一地址码区域内,同年、同月、同日生的人所编订的顺序号,根据自己身份证的顺序码就可以知道:与我们同年同月同日生的同性至少有多少个,且在我们之前登记户籍的有多少人。身份证顺序码的奇数分配给男性,偶数分配给女性。这就是为什么倒数第二位奇数表示男生,偶数表示女生。
1.1.4 校验码(R)
R之前的17位被称为本体码,R是根据本体码,按照校验码算法(ISO 7064:1983,MOD 11-2校)计算出来的。当我们输入身份号码进行实名认证的时候,根据校验码算法可以初步判断你输入身份证号码格式是否正确。
1.2 校验码算法
将本体码各位数字乘以对应加权因子并求和,除以11得到余数,根据余数通过校验码对照表查得校验码。
1.2.1 加权因子
位置序号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
加权因子 | 7 | 9 | 10 | 5 | 8 | 4 | 2 | 1 | 6 | 3 | 7 | 9 | 10 | 5 | 8 | 4 | 2 |
(本体码每个位置对应的加权因子)
1.2.2 校验码表
余数 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|
校验码 | 1 | 0 | X | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 |
(每个余数对应的校验码)
1.2.3 应用举例
某公民的身份证号码是34052419800101001X
第一步:本体码乘以加权因子:
3*7+4*9+0*10+……0*4+1*2=189
- 1
第二步:计算求和后除以11的余数
189%11=2
- 1
第三步:在检验码中查询余数对应的检验码
2所对应的校验码是X,注意X必须大写
2 Java编码实现
2.1 对外提供的调用接口
1 /** 2 * 二代身份证号码有效性校验 3 * 4 * @param idNo 5 * @return 6 */ 7 public static boolean isValidIdNo(String idNo) { 8 return isIdNoPattern(idNo) && isValidProvinceId(idNo.substring(0, 2)) 9 && isValidDate(idNo.substring(6, 14)) && checkIdNoLastNum(idNo); 10 }
2.2 二代身份证正则表达式
1 /** 2 * 二代身份证正则表达式 3 * 4 * @param idNo 5 * @return 6 */ 7 private static boolean isIdNoPattern(String idNo) { 8 return Pattern.matches("^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([\d|x|X]{1})$", idNo); 9 }
2.3 校验前两位省份信息
2.3.1 常量
1 //省(直辖市)码表 2 private static String provinceCode[] = { "11", "12", "13", "14", "15", "21", "22", 3 "23", "31", "32", "33", "34", "35", "36", "37", "41", "42", "43", 4 "44", "45", "46", "50", "51", "52", "53", "54", "61", "62", "63", 5 "64", "65", "71", "81", "82", "91" };
2.3.2 检查省份信息
1 /** 2 * 检查身份证的省份信息是否正确 3 * @param provinceId 4 * @return 5 */ 6 public static boolean isValidProvinceId(String provinceId){ 7 for (String id : provinceCode) { 8 if (id.equals(provinceId)) { 9 return true; 10 } 11 } 12 return false; 13 }
2.4 判断中间的六位日期是否有效
1 /** 2 * 判断日期是否有效 3 * @param inDate 4 * @return 5 */ 6 public static boolean isValidDate(String inDate) { 7 if (inDate == null){ 8 return false; 9 } 10 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); 11 if (inDate.trim().length() != dateFormat.toPattern().length()){ 12 return false; 13 } 14 dateFormat.setLenient(false);//执行严格的日期匹配 15 try { 16 dateFormat.parse(inDate.trim()); 17 } catch (ParseException e) { 18 return false; 19 } 20 return true; 21 }
2.4 校验第18位校验码
2.4.1 常量
1 //身份证前17位每位加权因子 2 private static int[] power = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; 3 4 //身份证第18位校检码 5 private static String[] refNumber ={"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};
2.4.2 计算第18位校验码
1 /** 2 * 计算身份证的第十八位校验码 3 * @param cardIdArray 4 * @return 5 */ 6 public static String sumPower(int[] cardIdArray){ 7 int result = 0; 8 for(int i=0;i<power.length;i++){ 9 result += power[i] * cardIdArray[i]; 10 } 11 return refNumber[(result%11)]; 12 }
2.4.2 验证第18位校验码是否正确
1 /** 2 * 校验身份证第18位是否正确(只适合18位身份证) 3 * @param idNo 4 * @return 5 */ 6 public static boolean checkIdNoLastNum(String idNo){ 7 if(idNo.length() != 18){ 8 return false; 9 } 10 char[] tmp = idNo.toCharArray(); 11 int[] cardidArray = new int[tmp.length-1]; 12 int i=0; 13 for(i=0;i<tmp.length-1;i++){ 14 cardidArray[i] = Integer.parseInt(tmp[i]+""); 15 } 16 String checkCode = sumPower(cardidArray); 17 String lastNum = tmp[tmp.length-1] + ""; 18 if(lastNum.equals("x")){ 19 lastNum = lastNum.toUpperCase(); 20 } 21 if(!checkCode.equals(lastNum)){ 22 return false; 23 } 24 return true; 25 }