• 比较全面的身份证校验、提取的方法


    提供下面方法:

    • 身份证号码快速校验
    • 身份证号码严格校验(较慢)
    • 身份证信息提取,返回省份、性别、出生日期

    用法:

    IdCardService service = new IdCardService();
    service.setIdCardNo("440105190009260215");
    IdCardInfoExtractor info = service.getInfo();
    System.out.println(info);
    
    System.out.println(service.fastTest());
    System.out.println(service.strictTest());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    IdCardService:

    public class IdCardService {
        /**
         * 身份证第18位校检码
         */
        static final String[] REF_NUMBER = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};
    
        /**
         * 身份证前17位每位加权因子
         */
        static final int[] POWER = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
    
        /**
         * 省(直辖市)码表
         */
        static final 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 String idCardNo;
    
        public void setIdCardNo(String idCardNo) {
            this.idCardNo = idCardNo;
        }
    
        public String getIdCardNo() {
            return idCardNo;
        }
    
        /**
         * 身份证信息提取
         *
         * @return
         */
        public IdCardInfoExtractor getInfo() {
            return new IdCardInfoExtractor(getIdCardNo());
        }
    
        /**
         * 快速检查
         *
         * @return true 表示为合法的身份证号码
         */
        public boolean fastTest() {
            return DetectIdCard.fastDetect(getIdCardNo());
        }
    
        /**
         * 严格检查
         *
         * @return true 表示为合法的身份证号码
         */
        public boolean strictTest() {
            return DetectIdCard.isValidIdNo(getIdCardNo());
        }
    
        /**
         * 将和值与11取模得到余数进行校验码判断
         *
         * @return 校验位
         */
        public static String getCheckCodeBySum(int sum17) {
            return REF_NUMBER[(sum17 % 11)];
        }
    
        /**
         * 检查身份证的省份信息是否正确
         */
        public static boolean isValidProvinceId(String provinceId) {
            for (String id : PROVINCE_CODE) {
                if (id.equals(provinceId))
                    return true;
            }
    
            return false;
        }
        
    }
    
    • 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

    DetectIdCard:

    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.regex.Pattern;
    
    /**
     * 严格检查
     *
     * @author ...
     */
    public class DetectIdCard {
        /**
         * 15位和18位身份证号码的基本数字和位数验校
         */
        public boolean isIdCard(String idCard) {
            return idCard != null && !"".equals(idCard) && Pattern.matches("(^\\d{15}$)|(\\d{17}(?:\\d|x|X)$)", idCard);
        }
    
        /**
         * 15位身份证号码的基本数字和位数验校
         */
        public boolean is15idCard(String idCard) {
            return idCard != null && !"".equals(idCard) && Pattern.matches("^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}$", idCard);
        }
    
        /**
         * 18位(二代)身份证正则表达式
         */
        public static boolean is18idCard(String idCard) {
            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})$", idCard);
        }
    
        /**
         * 数字验证
         */
        public boolean isDigital(String str) {
            return str != null && !"".equals(str) && str.matches("^[0-9]*$");
        }
    
        /**
         * 二代身份证号码有效性校验
         */
        public static boolean isValidIdNo(String idNo) {
            return is18idCard(idNo) && IdCardService.isValidProvinceId(idNo.substring(0, 2)) && isValidDate(idNo.substring(6, 14)) && checkIdNoLastNum(idNo);
        }
    
        /**
         * 判断日期是否有效
         */
        private static boolean isValidDate(String inDate) {
            if (inDate == null)
                return false;
    
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
            if (inDate.trim().length() != dateFormat.toPattern().length())
                return false;
    
            dateFormat.setLenient(false);// 执行严格的日期匹配
    
            try {
                dateFormat.parse(inDate.trim());
            } catch (ParseException e) {
                return false;
            }
    
            return true;
        }
    
        /**
         * 校验身份证第18位是否正确(只适合18位身份证)
         */
        private static boolean checkIdNoLastNum(String idNo) {
            if (idNo.length() != 18)
                return false;
    
            char[] tmp = idNo.toCharArray();
            int[] cardIdArray = new int[tmp.length - 1];
    
            for (int i = 0; i < tmp.length - 1; i++)
                cardIdArray[i] = Integer.parseInt(String.valueOf(tmp[i]));
    
            String checkCode = sumPower(cardIdArray);
            String lastNum = String.valueOf(tmp[tmp.length - 1]);
    
            if (lastNum.equals("x"))
                lastNum = lastNum.toUpperCase();
    
            return checkCode.equals(lastNum);
        }
    
    
        /**
         * 计算身份证的第18位校验码
         */
        private static String sumPower(int[] cardIdArray) {
            int result = 0;
    
            for (int i = 0; i < IdCardService.POWER.length; i++)
                result += IdCardService.POWER[i] * cardIdArray[i];
    
            return IdCardService.getCheckCodeBySum(result);
        }
    
        /**
         * 快速检测
         *
         * @author ...
         */
        public static boolean fastDetect(String id) {
            boolean flag = false;
            // 验证码
            String validateCode = id.substring(17, 18);
            // 前17位称为本体码
            String selfCode = id.substring(0, 17);
            String[] code = new String[17];
    
            for (int i = 0; i < 17; i++)
                code[i] = selfCode.substring(i, i + 1);
    
            // 加权因子公式:2的n-1次幂除以11取余数,n就是那个i,从右向左排列。
            int sum = 0; // 用于加权数求和
    
            for (int i = 0; i < code.length; i++) {
                int yi = adjustFactor(i + 1) % 11; // 计算该位加权因子
                int count = Integer.parseInt(code[code.length - i - 1]);  // 得到对应数位上的数字
                sum += (count * yi);  // 加权求和
            }
    
            // 验证校验码是否正确
            if (IdCardService.getCheckCodeBySum(sum).equalsIgnoreCase(validateCode))
                flag = true;
    
            return flag;
        }
    
        /**
         * 计算身份证数位数字加权因子
         *
         * @param digit 表示数位
         */
        private static int adjustFactor(int digit) {
            int sum = 1;
    
            for (int i = 0; i < digit; i++)
                // sum=sum*2;
                sum = sum << 1;
    
            return sum;
        }
    }
    
    • 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

    IdCardInfoExtractor:

    import lombok.Data;
    import lombok.EqualsAndHashCode;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.*;
    
    /**
     * 身份证合法性校验提取身份证相关信息
     * 

    * --15位身份证号码:第7、8位为出生年份(两位数),第9、10位为出生月份,第11、12位代表出生日期,第15位代表性别,奇数为男,偶数为女。 * --18位身份证号码 * 第7、8、9、10位为出生年份(四位数),第11、第12位为出生月份,第13、14位代表出生日期,第17位代表性别,奇数为男,偶数为女。 * * @author ... */ @EqualsAndHashCode(callSuper = true) @Data public class IdCardInfoExtractor extends DetectIdCard { // 省份 private String province; // 城市 private String city; // 区县 private String region; // 年份 private int year; // 月份 private int month; // 日期 private int day; // 性别 private String gender; // 出生日期 private Date birthday; private static final Map<String, String> cityCodeMap = new HashMap<String, String>() { private static final long serialVersionUID = 4593620948714292923L; { this.put("11", "北京"); this.put("12", "天津"); this.put("13", "河北"); this.put("14", "山西"); this.put("15", "内蒙古"); this.put("21", "辽宁"); this.put("22", "吉林"); this.put("23", "黑龙江"); this.put("31", "上海"); this.put("32", "江苏"); this.put("33", "浙江"); this.put("34", "安徽"); this.put("35", "福建"); this.put("36", "江西"); this.put("37", "山东"); this.put("41", "河南"); this.put("42", "湖北"); this.put("43", "湖南"); this.put("44", "广东"); this.put("45", "广西"); this.put("46", "海南"); this.put("50", "重庆"); this.put("51", "四川"); this.put("52", "贵州"); this.put("53", "云南"); this.put("54", "西藏"); this.put("61", "陕西"); this.put("62", "甘肃"); this.put("63", "青海"); this.put("64", "宁夏"); this.put("65", "新疆"); this.put("71", "台湾"); this.put("81", "香港"); this.put("82", "澳门"); this.put("91", "国外"); } }; public IdCardInfoExtractor() { } /** * 通过构造方法初始化各个成员属性 */ public IdCardInfoExtractor(String idCard) { if (isValidatedAllIdCard(idCard)) { if (idCard.length() == 15) idCard = convertCardBy15bit(idCard); String provinceId = idCard.substring(0, 2); // 获取省份 for (String id : cityCodeMap.keySet()) { if (id.equals(provinceId)) { province = cityCodeMap.get(id); break; } } // 获取性别 String id17 = idCard.substring(16, 17); gender = Integer.parseInt(id17) % 2 != 0 ? "男" : "女"; // 获取出生日期 try { birthday = new SimpleDateFormat("yyyyMMdd").parse(idCard.substring(6, 14)); } catch (ParseException e) { e.printStackTrace(); } GregorianCalendar currentDay = new GregorianCalendar(); currentDay.setTime(birthday); year = currentDay.get(Calendar.YEAR); month = currentDay.get(Calendar.MONTH) + 1; day = currentDay.get(Calendar.DAY_OF_MONTH); } } @Override public String toString() { return "省份:" + province + ",城市:" + city + ", 性别:" + gender + ", 出生日期:" + birthday; } /** * 验证所有的身份证的合法性 */ public boolean isValidatedAllIdCard(String idCard) { if (idCard.length() == 15) idCard = convertCardBy15bit(idCard); return isValidate18idCard(idCard); } /** *

    * 判断18位身份证的合法性 *

    * 根据〖中华人民共和国国家标准GB11643-1999〗中有关公民身份号码的规定,公民身份号码是特征组合码,由十七位数字本体码和一位数字校验码组成。 * 排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。 *

    * 顺序码: 表示在同一地址码所标识的区域范围内,对同年、同月、同 日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配 给女性。 *

    *

    * 1.前1、2位数字表示:所在省份的代码; 2.第3、4位数字表示:所在城市的代码; 3.第5、6位数字表示:所在区县的代码; * 4.第7~14位数字表示:出生年、月、日; 5.第15、16位数字表示:所在地的派出所的代码; 6.第17位数字表示性别:奇数表示男性,偶数表示女性; * 7.第18位数字是校检码:也有的说是个人信息码,一般是随计算机的随机产生,用来检验身份证的正确性。校检码可以是0~9的数字,有时也用x表示。 *

    *

    * 第十八位数字(校验码)的计算方法为: 1.将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7 9 10 5 8 4 2 1 * 6 3 7 9 10 5 8 4 2 *

    *

    * 2.将这17位数字和系数相乘的结果相加。 *

    *

    * 3.用加出来和除以11,看余数是多少? *

    * 4.余数只可能有0 1 2 3 4 5 6 7 8 9 10这11个数字。其分别对应的最后一位身份证的号码为1 0 X 9 8 7 6 5 4 3 2。 *

    * 5.通过上面得知如果余数是2,就会在身份证的第18位数字上出现罗马数字的Ⅹ。如果余数是10,身份证的最后一位号码就是2。 *

    */
    public boolean isValidate18idCard(String idCard) { // 非18位为假 if (idCard.length() != 18) return false; String idCard17 = idCard.substring(0, 17); // 获取前17位 if (!isDigital(idCard17)) // 是否都为数字 return false; String checkCode = getCheckCodeByIdCard17(idCard17); // 将和值与11取模得到余数进行校验码判断 if (checkCode == null) return false; return idCard.substring(17, 18).equalsIgnoreCase(checkCode);// 将身份证的第18位与算出来的校码进行匹配,不相等就为假 } /** * 验证15位身份证的合法性,该方法验证不准确,最好是将15转为18位后再判断,该类中已提供。 */ public boolean isValidate15idCard(String idCard) { // 非15位为假 if (idCard.length() != 15) return false; // 是否全都为数字 if (isDigital(idCard)) { String province = idCard.substring(0, 2), birthday = idCard.substring(6, 12); int year = Integer.parseInt(idCard.substring(6, 8)), month = Integer.parseInt(idCard.substring(8, 10)), day = Integer.parseInt(idCard.substring(10, 12)); // 判断是否为合法的省份 boolean flag = IdCardService.isValidProvinceId(province); if (!flag) return false; // 该身份证生出日期在当前日期之后时为假 Date birthdate = null; try { birthdate = new SimpleDateFormat("yyMMdd").parse(birthday); } catch (ParseException e) { e.printStackTrace(); } if (birthdate == null || new Date().before(birthdate)) return false; // 判断是否为合法的年份 GregorianCalendar curDay = new GregorianCalendar(); int curYear = curDay.get(Calendar.YEAR), year2bit = Integer.parseInt(String.valueOf(curYear).substring(2)); // 判断该年份的两位表示法,小于50的和大于当前年份的,为假 if ((year < 50 && year > year2bit)) return false; // 判断是否为合法的月份 if (month < 1 || month > 12) return false; // 判断是否为合法的日期 boolean mFlag = false; curDay.setTime(birthdate); // 将该身份证的出生日期赋于对象 curDay switch (month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: mFlag = (day >= 1 && day <= 31); break; case 2: // 公历的2月非闰年有28天,闰年的2月是29天。 if (curDay.isLeapYear(curDay.get(Calendar.YEAR))) { mFlag = (day >= 1 && day <= 29); } else { mFlag = (day >= 1 && day <= 28); } break; case 4: case 6: case 9: case 11: mFlag = (day >= 1 && day <= 30); break; } return mFlag; } else return false; } /** * 将15位的身份证转成18位身份证 */ public String convertCardBy15bit(String idCard) { String idCard17; // 非15位身份证 if (idCard.length() != 15) return null; if (isDigital(idCard)) { // 获取出生年月日 String birthday = idCard.substring(6, 12); Date birthdate = null; try { birthdate = new SimpleDateFormat("yyMMdd").parse(birthday); } catch (ParseException e) { e.printStackTrace(); } Calendar cDay = Calendar.getInstance(); assert birthdate != null; cDay.setTime(birthdate); String year = String.valueOf(cDay.get(Calendar.YEAR)); idCard17 = idCard.substring(0, 6) + year + idCard.substring(8); String checkCode = getCheckCodeByIdCard17(idCard17); // 获取和值与11取模得到余数进行校验码 if (checkCode == null) // 获取不到校验位 return null; idCard17 += checkCode; // 将前17位与第18位校验码拼接 } else // 身份证包含数字 return null; return idCard17; } public static String getCheckCodeByIdCard17(String idCard17) { int sum17 = getPowerSum(convertCharToInt(idCard17.toCharArray())); return IdCardService.getCheckCodeBySum(sum17); } /** * 将身份证的每位和对应位的加权因子相乘之后,再得到和值 */ private static int getPowerSum(int[] bit) { int sum = 0; if (IdCardService.POWER.length != bit.length) return sum; for (int i = 0; i < bit.length; i++) { for (int j = 0; j < IdCardService.POWER.length; j++) { if (i == j) sum += bit[i] * IdCardService.POWER[j]; } } return sum; } /** * 将字符数组转为整型数组 */ private static int[] convertCharToInt(char[] c) throws NumberFormatException { int[] a = new int[c.length]; int k = 0; for (char temp : c) a[k++] = Integer.parseInt(String.valueOf(temp)); return a; } }
    • 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
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332

    重构后的

    上述的代码都是网上 copy 回来的,包含了大量的重复,这不,我重构了一下,既保留了原有的功能,又更加精炼!——大家就用这个版本即可。

    import lombok.Data;
    import org.springframework.util.StringUtils;
    
    import java.time.LocalDate;
    import java.time.format.DateTimeFormatter;
    import java.time.format.DateTimeParseException;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.regex.Pattern;
    
    @Data
    public class IdCardService {
        /**
         * 身份证第18位校检码
         */
        private static final String[] REF_NUMBER = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};
    
        /**
         * 身份证前17位每位加权因子
         */
        private static final int[] POWER = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
    
        /**
         * 18位(二代)身份证正则表达式
         */
        private static final Pattern PATTERN = Pattern.compile("^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([012]\\d)|3[0-1])\\d{3}([\\dxX])$");
    
        private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
    
        private static final Map<String, String> PROVINCE_CODE_MAP = new HashMap<String, String>() {
            private static final long serialVersionUID = 4593620948714292923L;
    
            {
                put("11", "北京");
                put("12", "天津");
                put("13", "河北");
                put("14", "山西");
                put("15", "内蒙古");
                put("21", "辽宁");
                put("22", "吉林");
                put("23", "黑龙江");
                put("31", "上海");
                put("32", "江苏");
                put("33", "浙江");
                put("34", "安徽");
                put("35", "福建");
                put("36", "江西");
                put("37", "山东");
                put("41", "河南");
                put("42", "湖北");
                put("43", "湖南");
                put("44", "广东");
                put("45", "广西");
                put("46", "海南");
                put("50", "重庆");
                put("51", "四川");
                put("52", "贵州");
                put("53", "云南");
                put("54", "西藏");
                put("61", "陕西");
                put("62", "甘肃");
                put("63", "青海");
                put("64", "宁夏");
                put("65", "新疆");
                put("71", "台湾");
                put("81", "香港");
                put("82", "澳门");
                put("91", "国外");
            }
        };
    
        // 省份
        private String province;
        // 城市
        private String city;
        // 区县
        private String region;
        // 年份
        private int year;
        // 月份
        private int month;
        // 日期
        private int day;
        // 性别
        private String gender;
        // 出生日期
        private LocalDate birthday;
    
        /**
         * 身份证号
         */
        private String idNo;
    
        public IdCardService(String idNo) {
            this.idNo = idNo;
        }
    
        /**
         * 检查身份证号码有效性校验
         *
         * @return true 表示为合法的身份证号码
         */
        public boolean check() {
            return check(idNo);
        }
    
        /**
         * 身份证信息提取
         */
        public void extractor() {
            province = PROVINCE_CODE_MAP.get(idNo.substring(0, 2)); // 获取省份
            gender = Integer.parseInt(idNo.substring(16, 17)) % 2 != 0 ? "男" : "女";// 获取性别
            birthday = LocalDate.parse(idNo.substring(6, 14), FORMATTER); // 获取出生日期
            year = birthday.getYear();
            month = birthday.getMonthValue();
            day = birthday.getDayOfMonth();
        }
    
    
        /**
         * 严格检查
         * 二代身份证号码有效性校验
         *
         * @return true 表示为合法的身份证号码
         */
        public static boolean check(String idNo) {
            if (!StringUtils.hasText(idNo) || idNo.length() != 18 || !PATTERN.matcher(idNo).matches() || !PROVINCE_CODE_MAP.containsKey(idNo.substring(0, 2)) // 检查身份证的省份信息是否正确
            ) return false;
    
            try {
                LocalDate.parse(idNo.substring(6, 14), FORMATTER); // 判断日期是否有效
            } catch (DateTimeParseException e) {
                return false;
            }
    
            return checkIdNoLastNum(idNo);
        }
    
        public static void main(String[] args) {
            System.out.println(check("440105195303060115"));
        }
    
        /**
         * 校验身份证第18位是否正确
         */
        private static boolean checkIdNoLastNum(String idNo) {
            char[] tmp = idNo.toCharArray();
            int[] cardIdArray = new int[tmp.length - 1];
    
            for (int i = 0; i < tmp.length - 1; i++)
                cardIdArray[i] = Integer.parseInt(String.valueOf(tmp[i]));
    
            int sum17 = 0;
    
            for (int i = 0; i < POWER.length; i++)
                sum17 += POWER[i] * cardIdArray[i];
    
            return idNo.substring(17, 18).equalsIgnoreCase(REF_NUMBER[(sum17 % 11)]); // 将和值与11取模得到余数进行校验码判断
        }
    
        /**
         * 将和值与11取模得到余数进行校验码判断
         *
         * @return 校验位
         */
        private static String getCheckCodeBySum(int sum) {
            return REF_NUMBER[(sum % 11)];
        }
    
        /**
         * 快速检测
         *
         * @author ...
         */
        public static boolean check2(String idNo) {
            // 前17位称为本体码
            String selfCode = idNo.substring(0, 17);
            String[] code = new String[17];
    
            for (int i = 0; i < 17; i++)
                code[i] = selfCode.substring(i, i + 1);
    
            // 加权因子公式:2的n-1次幂除以11取余数,n就是那个i,从右向左排列。
            int sum17 = 0; // 用于加权数求和
    
            for (int i = 0; i < code.length; i++) {
                int yi = adjustFactor(i + 1) % 11; // 计算该位加权因子
                int count = Integer.parseInt(code[code.length - i - 1]);  // 得到对应数位上的数字
                sum17 += (count * yi);  // 加权求和
            }
    
            // 验证校验码是否正确
            return idNo.substring(17, 18).equalsIgnoreCase(REF_NUMBER[(sum17 % 11)]);
        }
    
        /**
         * 计算身份证数位数字加权因子
         *
         * @param digit 表示数位
         */
        private static int adjustFactor(int digit) {
            int sum = 1;
    
            for (int i = 0; i < digit; i++)
                // sum=sum*2;
                sum = sum << 1;
    
            return sum;
        }
    
    }
    
    • 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
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211

    小插曲

    遇到这大神写的代码,甚是精炼:

    String src = "18位号码";
    
    char[] cs = src.toCharArray();
    
    int x = (c2i(cs[0]) + c2i(cs[10])) * 7
     + (c2i(cs[1]) + c2i(cs[11])) * 9
     + (c2i(cs[2]) + c2i(cs[12])) * 10
     + (c2i(cs[3]) + c2i(cs[13])) * 5
     + (c2i(cs[4]) + c2i(cs[14])) * 8
     + (c2i(cs[5]) + c2i(cs[15])) * 4
     + (c2i(cs[6]) + c2i(cs[16])) * 2
     + c2i(cs[7]) * 1
     + c2i(cs[8]) * 6
     + c2i(cs[9]) * 3 ;
    
    x%= 11;
    
    return src.charAt(17) == "10X98765432".charAt(x);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这段代码是一个用于验证中国身份证号码是否合法的 Java 程序。它通过对身份证号码的前17位进行加权求和,并根据计算结果与最后一位校验码进行比较来判断身份证号码的合法性。

    具体的验证过程如下:

    1. 将身份证号码转换为字符数组,方便按索引访问每一位数字。
    2. 根据身份证号码的前 17 位进行加权求和。加权系数分别为 7、9、10、5、8、4、2、1、6、3。加权求和的方式是将每一位数字与对应的加权系数相乘,然后将所有结果相加得到一个总和 x。
    3. 将 x 除以 11 取余数,得到一个余数值。
    4. 通过比较身份证号码的最后一位校验码与"10X98765432"字符串中索引为余数值的字符是否相等,来确定身份证号码的合法性。如果相等,则返回true;否则,返回false

    需要注意的是,这段代码只能验证身份证号码是否符合基本规则,不能验证号码是否真实存在或与持有人匹配。

    另外,代码中引用的c2i函数可能是一个自定义的方法,用于将字符转换为对应的数字值。我补充一下c2i的具体实现,可能是根据 ASCII 码来进行转换的。

    public static int c2i(char c) {
        if (c >= '0' && c <= '9') {
            return c - '0';
        } else {
            throw new IllegalArgumentException("Invalid character: " + c);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这个c2i函数接受一个字符作为参数,并将其转换为对应的整数值。它首先检查字符是否在数字字符的范围内(‘0’到’9’),如果是,则通过减去字符’0’得到对应的数字值。如果字符不在这个范围内,则抛出一个IllegalArgumentException异常,表示传入的字符无效。

  • 相关阅读:
    Softing连接解决方案——将FANUC数控机床数据集成到西门子工业边缘
    阿米巴经营管理模式是什么,能做什么,有什么好处和坏处?
    学习笔记-角色权限
    Spring的事务传播机制
    垃圾分类网站 |基于springboot框架+ Mysql+Java+B/S结构的垃圾分类网站 设计与实现(可运行源码+数据库+设计文档+部署说明)
    WPF绘图(一):几何(Geometry)与形状(Shape)
    《安富莱嵌入式周报》第283期:全开源逆向“爆破”硬件工具,Linux内核6.1将正式引入RUST语言,I3C培训教程,80款市场成熟的电感式位置传感器设计
    如何使用Map处理Dom节点
    一文教你如何使用Scan Kit快速生成带有logo的个性化二维码
    golang--swagger集成
  • 原文地址:https://blog.csdn.net/zhangxin09/article/details/132898938