一、前言
身份证结构和形式
1.号码的结构
公民身份号码是特征组合码,由十七位数字本体码和一位校验码组成。排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。
2.地址码
表示编码对象常住户口所在县(县级市、旗、区)的行政区划代码,按GB/T2260的规定执行。
3.出生日期码
表示编码对象出生的年、月、日,按GB/T7408的规定执行,年、月、日代码之间不用分隔符。
4.顺序码
表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。
5.校验码
根据前面十七位数字码,按照ISO 7064:1983.MOD 11-2校验码计算出来的检验码。当校验码为10时,用罗马数字X表示。
二、身份证校验代码
package com.zlc.common.utils;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class IDCardCheck {
/**
* 前十七位加权因子
*/
private static int[] idCardWi = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
/**
* 这是除以11后,可能产生的11位余数对应的验证码
*/
private static String[] idCardY = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};
/**
* 15位身份证生日前缀
*/
private static String CENTURY = "19";
/**
* 身份证性别中文描述
*/
private static String[] CHINESS_SEX = {"女", "男"};
/**
* 18\15位
*/
private static int IDCARD_LENTH_18 = 18;
private static int IDCARD_LENTH_15 = 15;
/**
* 对应省市
*/
private static List<Province> provinces = new ArrayList<>(35);
static {
provinces.add(new Province("11", "北京"));
provinces.add(new Province("12", "天津"));
provinces.add(new Province("13", "河北"));
provinces.add(new Province("14", "山西"));
provinces.add(new Province("15", "内蒙古"));
provinces.add(new Province("21", "辽宁"));
provinces.add(new Province("22", "吉林"));
provinces.add(new Province("23", "黑龙江"));
provinces.add(new Province("31", "上海"));
provinces.add(new Province("32", "江苏"));
provinces.add(new Province("33", "浙江"));
provinces.add(new Province("34", "安徽"));
provinces.add(new Province("35", "福建"));
provinces.add(new Province("36", "江西"));
provinces.add(new Province("37", "山东"));
provinces.add(new Province("41", "河南"));
provinces.add(new Province("42", "湖北"));
provinces.add(new Province("43", "湖南"));
provinces.add(new Province("44", "广东"));
provinces.add(new Province("45", "广西"));
provinces.add(new Province("46", "海南"));
provinces.add(new Province("50", "重庆"));
provinces.add(new Province("51", "四川"));
provinces.add(new Province("52", "贵州"));
provinces.add(new Province("53", "云南"));
provinces.add(new Province("54", "西藏"));
provinces.add(new Province("61", "陕西"));
provinces.add(new Province("62", "甘肃"));
provinces.add(new Province("63", "青海"));
provinces.add(new Province("64", "宁夏"));
provinces.add(new Province("65", "新疆"));
provinces.add(new Province("71", "台湾"));
provinces.add(new Province("81", "香港"));
provinces.add(new Province("82", "澳门"));
provinces.add(new Province("91", "国外"));
}
/**
* 从身份证中获取性别
* 0:女
* 1:男
*
* @param idcard 身份证号
* @return int
*/
public static int getSex(String idcard) {
int len = idcard.length();
if (len == IDCARD_LENTH_18) {
return Integer.parseInt(idcard.substring(16).substring(0, 1)) % 2;
} else {
return Integer.parseInt(idcard.substring(14, 15)) % 2;
}
}
/**
* 获得出身日期
*
* @param idcard 身份证号
* @return LocalDate
*/
public static LocalDate getBirthday(String idcard) {
int len = idcard.length();
if (len == IDCARD_LENTH_18) {
return LocalDate.parse(idcard.substring(6, 14), DateTimeFormatter.ofPattern("yyyyMMdd"));
} else {
return LocalDate.parse(CENTURY + idcard.substring(6, 12), DateTimeFormatter.ofPattern("yyyyMMdd"));
}
}
/**
* 获得年龄
*
* @param idcard 身份证号
* @return long
*/
public static long getAge(String idcard) {
return IDCardCheck.getBirthday(idcard).until(LocalDate.now(), ChronoUnit.YEARS);
}
/**
* 获取性别中文标志
*/
public static String getChineseSex(String idcard) {
return CHINESS_SEX[getSex(idcard)];
}
/**
* 获取对应省份
*
* @param idcard 身份证号
* @return String
*/
public static String getProvince(String idcard) {
String provinceId = idcard.substring(0, 2);
Optional<Province> province = provinces.stream().filter(e -> e.getId().equals(provinceId)).findFirst();
return province.map(province1 -> ((Province) province1).getName()).orElse(null);
}
/**
* 身份证校验 18和15位
*
* @param idcard 身份证号
* @return boolean
*/
public static boolean checkIDCard(String idcard) {
String regularExpression = "(^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$)|(^[1-9]\\d{5}\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}$)";
boolean isTrue = idcard.matches(regularExpression);
if (isTrue) {
if (idcard.length() == IDCARD_LENTH_18) {
char[] charArray = idcard.toCharArray();
int sum = 0;
int k = idCardWi.length;
for (int i = 0; i < k; i++) {
int current = Integer.parseInt(String.valueOf(charArray[i]));
int count = current * idCardWi[i];
sum += count;
}
char idCardLast = charArray[17];
int idCardMod = sum % 11;
return idCardY[idCardMod].toUpperCase().equals(String.valueOf(idCardLast).toUpperCase());
} else {
return true;
}
} else {
return false;
}
}
/**
* 18为身份证转15位
*
* @param idcard 身份证号
* @return String
*/
public static String turnTo15(String idcard) {
if (idcard.length() == IDCARD_LENTH_15) {
return idcard;
}
return idcard.substring(0, 6) + idcard.substring(8, 17);
}
/**
* 15位转18位
*
* @param idcard 身份证
* @return String
*/
public static String turnTo18(String idcard) {
if (idcard.length() == IDCARD_LENTH_18) {
return idcard;
}
// 加上两位年19
String id17 = idcard.substring(0, 6) + CENTURY + idcard.substring(6);
// 十七位数字本体码加权求和
int sum = 0;
int len = 17;
for (int i = 0; i < len; i++) {
sum = sum + Integer.valueOf(id17.substring(i, i + 1)) * idCardWi[i];
}
// 计算模
int y = sum % 11;
// 通过模得到对应的校验码
return id17 + idCardY[y];
}
}