扯淡区:
本博客主要内容是利用Java语言来验证身份证号码是否符合18位的二代身份证号规范.
公民身份号码是特征组合码,由十七位数字本体码和一位数字校验码组成。排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。可以用字母表示如为 ABCDEFYYYYMMDDXXXR
1. 地址码(ABCDEF):表示编码对象常住户口所在县(市、旗、区)的行政区划代码,按 GB/T2260 的规定执行。
2. 出生日期码(YYYYMMDD):表示编码对象出生的年、月、日,按 GB/T7408 的规定执行,年、月、日分别用 4 位、2 位(不足两位加 0)、2(同上)位数字表示,之间不用分隔符。
3. 顺序码(XXX):表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。
4.校验码(R):一位数字,通过前 17 位数字按照 ISO 7064:1983.MOD 11-2 校验码计算得出。
(以上内容为度娘友情提供,灰机票)
代码区:
首先列一下程序中需要用到的包:
// 文件部分 import java.io.File; import java.io.IOException; // 日期部分 import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; // 容器部分 import java.util.ArrayList; import java.util.List; // 正则表达式部分 import java.util.regex.Matcher; import java.util.regex.Pattern; // 读取用户输入 import java.util.Scanner; // Excel操作部分 import jxl.Sheet; import jxl.Workbook; import jxl.read.biff.BiffException;
不大会用面向对象%>_<%,只是写了个工具类
整个验证过程是分开写的,本来想每次都返回一些信息,结果写着写着就懒了,只是返回的布尔值,开头列了些信息差点没有没用上
public final static String VCODE1 = "1"; // 验证通过 public final static String VCODE2 = "2"; // 格式错误 public final static String VCODE3 = "3"; // 出生日期验证失败 public final static String VCODE4 = "4"; // 指定行政区划不存在 public final static String VCODE5 = "5"; // 校验码验证失败
身份证号验证之格式验证
身份证号应该是18位数字或17位数字+一个字母X,这里是通过正则表达式验证的(这里主要是练习在Java中使用正则表达式)
/** * 格式验证 * @param idCard * @return isQualified */ public boolean formatVerify (String idCard) { Pattern pattern = Pattern.compile("^(\\d{17})([0-9]|X)$"); // \d 开头 \d 匹配数字 {17} 17个字符 $ 结尾 Matcher matcher = pattern.matcher(idCard.toUpperCase()); // 大写并开始匹配 if (matcher.matches()) { return true; // 验证通过 } else { return false; // 格式错误 } }
身份证号验证之出生日期验证
出生日期应晚于1900,早于当前日期,并且是合法的 (这里主要是练习日期与字符串之间的转换)
/** * 身份证出生日期验证 * @param idCard * @return isQualified */ public boolean birthVerify (String idCard) { Date startDate = new Date(-2209017600000L); // 身份证的最小出生日期,1900年1月1日 Date stopDate = new Date(); // 当前日期 String birthStr = idCard.substring(6, 14); // 从身份证号取出出生日期 DateFormat format = new SimpleDateFormat("yyyyMMdd"); // 指定格式 format.setLenient(false); // 指定日期/时间解析是否不严格。 try { Date birth = format.parse(birthStr); // 将出生日期转换成Date // 验证出生日期是否晚于1900年早于当前日期 if (birth.after(startDate) && birth.before(stopDate)) { return true; // 验证通过 } else { return false; // 出生日期验证失败 } } catch (ParseException e) { //e.printStackTrace(); return false; // 出生日期验证失败 } }
身份证号验证之行政区划验证
每个地区国家都规定了行政区划代码,最新的行政区划代码可以从here查看(我把行政区划code和名称存放到了excel里面,直接读取,所以在这里我们练习的主要是Excel读取操作)
/** * 行政区划验证 * @param idCard * @return */ public boolean divisionVerify (String idCard) { try { Workbook wbk = Workbook.getWorkbook(new File("resource/division.xls")); // 读取行政区划文件 Sheet sheet = wbk.getSheet(0); // 获取默认的工作表 List<String> divisionList = new ArrayList<String>(); // 存储行政区划 for (int i = 0; i < sheet.getRows(); i++) { divisionList.add(sheet.getCell(4, i).getContents().trim()); // 获取单元格内容 4 代表列 i代表行 } String divisionStr = idCard.substring(0, 6); // 取出用户的行政区划代码 int divisionIndex = 0; // 将身份证号里的行政区划编号匹配已有行政区划 for (String division : divisionList) { divisionIndex++; if (divisionStr.equals(division)) // 判断该行政区划是否存在 break; } // 通过变量divisionIndex判断该行政区划是否存在 if (divisionIndex <= divisionList.size()) { return true; // 验证通过 } else { return false; // 指定行政区划不存在 } } catch (BiffException e) { //e.printStackTrace(); return false; // 文件加载异常 } catch (IOException e) { //e.printStackTrace(); return false; // 文件加载异常 } }
身份证号验证之校验值验证
数学不好,刚开始没看懂,后来在网上搜了搜,看了些代码才明白,解释给数学同样不好的小伙伴们,
规定有三个数组,其中一个有17个数{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2},要求我们将身份证号的每个值逐个与该数组相应位置的元素的乘积求和,(idCard:371326199211091234)形如:S=3*9+7*9+1+10...+3*4+4*2,然后将得出的和S去除以11取得余数Y,即取模,这时候第二个数组就开始起作用了,第二个数组类似于数组下标{0,1,2,3,4,5,6,7,8,9},而第三个数组则是最后的校验值{'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'},我们只要在这个数组中下标为Y的值即可。(我特么有晕了,这里没练什么,练习如何不晕)
/** * 校验值验证 * (1)十七位数字本体码加权求和公式 S = Sum(Ai * Wi), i = 0, ... , 16 ,先对前17位数字的权求和 * Ai:表示第i位置上的身份证号码数字值 Wi:表示第i位置上的加权因子 Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2 * (2)计算模 Y = mod(S, 11) * (3)通过模得到对应的校验码 Y: 0 1 2 3 4 5 6 7 8 9 校验码: 1 0 X 9 8 7 6 5 4 3 2 */ public boolean quanVerify (String idCard) { int s = 0; // 十七位数字本体码加权和 int[] wi = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; // 加权因子 char[] valiCodes = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'}; // 校验码 // 十七位数字本体码加权求和 for (int i = 0; i < 17; i++) { s += Integer.parseInt(String.valueOf(idCard.charAt(i))) * wi[i]; } char valiCode = valiCodes[s % 11]; if (idCard.toUpperCase().charAt(17) == valiCode) { return true; // 验证通过 } else { return false; // 校验码验证失败 } }
身份证号验证之整合
接着上面的晕乎劲儿,把这几部分串起来(这里练习的只有一个三木运算符)
/** * 身份证验证 */ public String idCardVerify (String idCard) { // 首先验证格式,通过的话在验证出生日期,通过的话在验证行政区划,再通过的话验证校验值 String code = !formatVerify(idCard) ? vCode2 : !birthVerify(idCard) ? vCode3 : !divisionVerify(idCard) ? vCode4 : !quanVerify(idCard) ? vCode5 : vCode1; return code; }
完事,写个main方法验证一下
public static void main(String[] args) { IdCardValidate validater = new IdCardValidate(); Scanner sc = new Scanner(System.in); while (true) { System.out.print("请输入要验证的身份证号(键入quit退出程序):"); // 提示 String idCard = sc.nextLine().trim(); // 读取用户输入 if ("quit".equalsIgnoreCase(idCard)) { System.out.println("谢谢使用!"); break; } String vCode = validater.idCardVerify(idCard); // 验证 // 解析结果 switch (vCode) { case "1" : System.out.println("验证通过"); break; case "2" : System.out.println("格式错误"); break; case "3" : System.out.println("出生日期验证失败"); break; case "4" : System.out.println("指定行政区划不存在"); break; case "5" : System.out.println("校验码验证失败"); break; } } }
好了,身份证号的验证做完了,虽然只是简单验证了下,不过东拼西凑啥都用了些,可以练练手啊。下次在做的时候可以把信息收集起来,如果验证通过的话可以把生日住址啥的列出来
附:
源码:
百度云 : http://pan.baidu.com/s/1kT41ysv
不积跬步,无以至千里;不积小流,无以成江海。