实现身份证校验

简单的了解一下省份证的组成:第1~6位是地址码,第7~14是出生年月日,第15~17位是顺序码,第18位是校验码。

简单分析一下18位的含义:第1~2位行政代码,第3~4位低级行政区划分代码,第5~6位县级行政区分代码,第7~10位、第11~12位、第13~14位分别表示出生年、月、日,第15~17位是顺序码,第17位还可以判断一个人的性别,第18位是校验码。

具体详情请点击:1、第二代身份证号码编排规则  2、身份证号码编排规则及校验算法

具体校验实现代码如下:

import java.util.Arrays;
import java.util.Calendar;

public class IDCardUtils {
    /**
     * 省,直辖市代码表: { 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:"云南",54:"西藏",61:"陕西",62:"甘肃",
     * 63:"青海",64:"宁夏",65:"新疆",71:"台湾",81:"香港",82:"澳门",91:"国外"}
     */
    protected String provinceAndCode[][] = {{"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", "云南"}, {"54", "西藏"},
            {"61", "陕西"}, {"62", "甘肃"}, {"63", "青海"}, {"64", "宁夏"},
            {"65", "新疆"}, {"71", "台湾"}, {"81", "香港"}, {"82", "澳门"},
            {"91", "国外"}};
    private static String PROVINCE_CODE[] = {"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", "54", "61", "62",
            "63", "64", "65", "71", "81", "82", "91"};

    private static String ERROR_COMMON = "您输入的身份证%s,请核对后再输";
    private static String REGEX_MOUTH_DAY = "(((0[13578]|1[02])(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)(0[1-9]|[12][0-9]|30))|(02(0[1-9]|[1][0-9]|2[0-9])))";
    // 日期判断 平年和闰年 2月的天数
    private static String REGEX_CARD_1 = "^[1-9]\\d{5}[1-9]\\d{3}" + REGEX_MOUTH_DAY + "\\d{3}([0-9]|X|x)$";// 3 比较精确的
    private String REGEX_CARD_2 = "^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9]|X|x)$";// 1
    // 身份证验证 15 18
    private static String REGEX_CARD_3 = "^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}$|^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9]|X|x)$";// 2
    // 每位加权因子
    private static int POWER[] = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
    // 第18位校检码
    private static String VERIFY_CODE[] = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};

    /**
     * 判断身份证是否符合身份证规则
     *
     * @param card      String
     * @param onlyRegex true:只保证合法性,不能保证有效性;false:合法性和有效性
     * @return String
     */
    public static String isCard(String card, boolean onlyRegex) {
        String msg;
        if (null == card || card.trim().equals("")) {
            msg = String.format(ERROR_COMMON, "为空");
        } else if (card.length() != 18) {
            msg = String.format(ERROR_COMMON, "位数有误");
        } else if (!card.matches(REGEX_CARD_1)) {// 符合身份证编码正则的
            msg = String.format(ERROR_COMMON, "有误");
        } else if (onlyRegex) {// 是符合身份证编码正则的
            msg = "true";
        } else if (!checkCityCode(getIDCardCityCode(card))) { // 获取前两位地址码并校验
            msg = String.format(ERROR_COMMON, "地址编码不对");
        } else if (-1 == checkCardYear(card)) {// 校验合法年份
            msg = String.format(ERROR_COMMON, "年份有误");
        } else if (-1 == checkCardMonth(card)) {
            msg = String.format(ERROR_COMMON, "月份有误");
        } else if (-1 == checkCardDay(card)) {
            msg = String.format(ERROR_COMMON, "日期有误");
        } else if (getIDCardCheckCode(card).equals(getComputeCardCheckCode(card))) {
            msg = "true";
        } else {
            msg = String.format(ERROR_COMMON, "无效");
        }
        return msg;
    }

    /**
     * 根据身份证17位计算出校验码
     *
     * @param IDCard
     * @return
     */
    public static String getComputeCardCheckCode(String IDCard) {
        return VERIFY_CODE[getPowerSum(IDCard) % 11];
    }

    /**
     * 获取出生日期
     *
     * @param IDCard
     * @return
     */
    public static String getBirthday(String IDCard) {
        return getStringForCard(IDCard, 6, 14);
    }

    /**
     * 获取身份证号校验码
     *
     * @param IDCard
     * @return
     */
    public static String getIDCardCheckCode(String IDCard) {
        return getStringForCard(IDCard, 17, 18);
    }

    /**
     * 获取日期
     *
     * @param IDCard
     * @return
     */
    public static int getIDCardDay(String IDCard) {
        return Integer.parseInt(getStringForCard(IDCard, 12, 14));
    }

    /**
     * 获取月份
     *
     * @param IDCard
     * @return
     */
    public static int getIDCardMonth(String IDCard) {
        return Integer.parseInt(getStringForCard(IDCard, 10, 12));
    }

    /**
     * 获取年份
     *
     * @param IDCard
     * @return
     */
    public static int getIDCardYear(String IDCard) {
        return Integer.parseInt(getStringForCard(IDCard, 6, 10));
    }

    /**
     * 获取区域编码
     *
     * @param IDCard
     * @return
     */
    public static String getIDCardCityCode(String IDCard) {
        return getStringForCard(IDCard, 0, 2);
    }

    /**
     * 截取数据
     *
     * @param IDCard
     * @param start
     * @param end
     * @return
     */
    public static String getStringForCard(String IDCard, int start, int end) {
        if (null != IDCard && !IDCard.trim().equals("") && IDCard.length() >= end) {
            return IDCard.substring(start, end);
        } else {
            return "-1";
        }
    }

    /**
     * 有效年份
     *
     * @param card
     * @return
     */
    public static int checkCardYear(String card) {
        Calendar calendar = Calendar.getInstance();
        int cardYear = getIDCardYear(card);
        if (-1 != cardYear) {
            int currentYear = calendar.get(Calendar.YEAR);// 获取年份
            return Integer.compare(currentYear, cardYear) == 0 ? 0 : currentYear > cardYear ? 1 : -1;
        }
        return -1;
    }

    /**
     * 有效月份
     *
     * @param card
     * @return
     */
    public static int checkCardMonth(String card) {
        Calendar calendar = Calendar.getInstance();
        int currentMonth = calendar.get(Calendar.MONTH) + 1;
        int yearCheck = checkCardYear(card);
        int cardMonth = getIDCardMonth(card);
        if (-1 == cardMonth) {
            return -1;
        } else if (yearCheck == 0) {
            return Integer.compare(currentMonth, cardMonth) == 0 ? 0 : currentMonth > cardMonth ? 1 : -1;
        } else {
            return yearCheck;
        }
    }

    /**
     * 有效日期
     *
     * @param card
     * @return
     */
    public static int checkCardDay(String card) {
        Calendar calendar = Calendar.getInstance();
        int monthCheck = checkCardMonth(card);
        int currentDay = calendar.get(Calendar.DAY_OF_MONTH);
        int cardDay = getIDCardDay(card);
        if (-1 == cardDay) {
            return -1;
        } else if (monthCheck == 0 && 1 == checkLeapYearDay(getIDCardYear(card), getIDCardMonth(card), cardDay)) {
            return Integer.compare(currentDay, cardDay) == 0 ? 0 : currentDay > cardDay ? 1 : -1;
        } else if (monthCheck == 1) {
            return checkLeapYearDay(getIDCardYear(card), getIDCardMonth(card), cardDay);
        } else {
            return monthCheck;
        }
    }

    /**
     * 校验闰年02月日期
     */
    private static int checkLeapYearDay(int cardYear, int cardMonth, int cardDay) {
        return !isLeapYear(cardYear) && 2 == cardMonth && cardDay == 29 ? -1 : 1;
    }

    /**
     * 校验省级代码
     *
     * @param cityCode
     * @return
     */
    public static boolean checkCityCode(String cityCode) {
        return Arrays.asList(PROVINCE_CODE).contains(cityCode);
    }

    /**
     * 判断 平闰年
     *
     * @param year
     * @return
     */
    public static boolean isLeapYear(int year) {
        return (year % 4 == 0 && year % 100 != 0 || year % 400 == 0);
    }

    /**
     * 获取性别
     *
     * @param IDCard
     * @return 1:男;0:女
     */
    public static int getIDCardGender(String IDCard) {
        return Integer.parseInt(getStringForCard(IDCard, 16, 17)) % 2;
    }

    /**
     * 将身份证的每位和对应位的加权因子相乘之后,再得到和值
     *
     * @param IDCard
     * @return
     */
    private static int getPowerSum(String IDCard) {
        int sum = 0;
        if (IDCard.length() < 18 || POWER.length != IDCard.length() - 1) {
            return sum;
        }
        for (int i = 0; i < POWER.length; i++) {
            sum = sum + Integer.parseInt(IDCard.substring(i, i + 1)) * POWER[i];
        }
        return sum;
    }
}




测试代码:

    @Test // 测试身份证号
    public void testRegexIDCard() {
        String[] cards = {
                // 有效数据
                "44522119940201887X", "15010219810707645X", "371326197911207560",
                // 年份
                "44522120180201887X", "15010220190707645X", "371326201811207560", "44522120180229887X",
                // 错误测试数据
                "87522119940201887X", "15010229810707645X", "371326197921207560", ""
        };
        for (int i = 0; i < cards.length; i++) {
            String temp = cards[i];
            System.out.println("精确校验:" + temp + " " + IDCardUtils.isCard(temp, false)
                    + " 性别:" + IDCardUtils.getIDCardGender(temp) + " 生日:" + IDCardUtils.getBirthday(temp)
                    + " 年:" + IDCardUtils.checkCardYear(temp) + " 月:" + IDCardUtils.checkCardMonth(temp) + " 日:" + IDCardUtils.checkCardDay(temp));
            System.out.println("正则校验:" +temp + " " + IDCardUtils.isCard(temp, true)
                    + " 性别:" + IDCardUtils.getIDCardGender(temp) +" 生日:" + IDCardUtils.getBirthday(temp)
                    + " 年:" + IDCardUtils.checkCardYear(temp) + " 月:" + IDCardUtils.checkCardMonth(temp) + " 日:" + IDCardUtils.checkCardDay(temp));
        }
    }



测试结果:

精确校验:44522119940201887X true 性别:1 生日:19940201 年:1 月:1 日:1
正则校验:44522119940201887X true 性别:1 生日:19940201 年:1 月:1 日:1
精确校验:15010219810707645X true 性别:1 生日:19810707 年:1 月:1 日:1
正则校验:15010219810707645X true 性别:1 生日:19810707 年:1 月:1 日:1
精确校验:371326197911207560 true 性别:0 生日:19791120 年:1 月:1 日:1
正则校验:371326197911207560 true 性别:0 生日:19791120 年:1 月:1 日:1
精确校验:44522120180201887X 您输入的身份证无效,请核对后再输 性别:1 生日:20180201 年:0 月:1 日:1
正则校验:44522120180201887X true 性别:1 生日:20180201 年:0 月:1 日:1
精确校验:15010220190707645X 您输入的身份证年份有误,请核对后再输 性别:1 生日:20190707 年:-1 月:-1 日:-1
正则校验:15010220190707645X true 性别:1 生日:20190707 年:-1 月:-1 日:-1
精确校验:371326201811207560 您输入的身份证月份有误,请核对后再输 性别:0 生日:20181120 年:0 月:-1 日:-1
正则校验:371326201811207560 true 性别:0 生日:20181120 年:0 月:-1 日:-1
精确校验:44522120180229887X 您输入的身份证日期有误,请核对后再输 性别:1 生日:20180229 年:0 月:1 日:-1
正则校验:44522120180229887X true 性别:1 生日:20180229 年:0 月:1 日:-1
精确校验:87522119940201887X 您输入的身份证地址编码不对,请核对后再输 性别:1 生日:19940201 年:1 月:1 日:1
正则校验:87522119940201887X true 性别:1 生日:19940201 年:1 月:1 日:1
精确校验:15010229810707645X 您输入的身份证年份有误,请核对后再输 性别:1 生日:29810707 年:-1 月:-1 日:-1
正则校验:15010229810707645X true 性别:1 生日:29810707 年:-1 月:-1 日:-1
精确校验:371326197921207560 您输入的身份证有误,请核对后再输 性别:0 生日:19792120 年:1 月:1 日:1
正则校验:371326197921207560 您输入的身份证有误,请核对后再输 性别:0 生日:19792120 年:1 月:1 日:1
精确校验: 您输入的身份证为空,请核对后再输 性别:-1 生日:-1 年:-1 月:-1 日:-1
正则校验: 您输入的身份证为空,请核对后再输 性别:-1 生日:-1 年:-1 月:-1 日:-1








                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值