简要
身份证号合法性判断:
若为18位身份证号:
1.第18位数字,应该在身份证号最后一位校验位数字中
2.前17为数字分别乘以对应的加权因子,累加,结果%11==17为数字
3.数字除18位外,全为数字
若位15位身份证号:
1.验证前6位是为行政区号
2.9-10位为月份<13
3.11-12位为日期<32
4.数据全为数字
注意:数据行政区号全部展示与代码中浏览器中打开时,会导致加载过慢卡顿,所以仅上传了部分数据,全量有*3271条行政区号,需要的评论留言
代码
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDF;
import java.util.*;
public class UdfIsIdcard extends UDF {
/**
* 身份证合法性判断自定义函数
* 支持15位和18位判断
*/
/**
*/
@Description(
name = "udf_is_idcard",
value = "_FUNC_(str) - Returns str is idcard?0:1",
extended = "Example:\n > SELECT _FUNC_(\'620121199701232548\') FROM dual LIMIT 1;\n return 0 "
)
static int[] weights = {7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2}; // 新版身份证号 1-17 位的加权因子. 该数组顺序不能变,和index有对应关系
static String[] checkCode = {"1","0","X","9","8","7","6","5","4","3","2"}; // 新身份证号最后一位校验位的值. 该数组顺序不能变,和index有对应关系
static Set<String> newIdCardNoLastNOSet = new HashSet<String>(Arrays.asList(checkCode));
static Set<String> numberSet = new HashSet<String>(Arrays.asList("9","8","7","6","5","4","3","2","1","0"));
static Set<String> areaCodeSet = new HashSet<String>(Arrays.asList("110101","110102","110105"));
public int evaluate(final String idCardNo) {
if (!basicLegality(idCardNo.trim())) { // 身份证号基础校验不通过,返回false
return 0;
}
if (oldIdCardNoValidate(idCardNo) || newIdCardNoValidate(idCardNo)) {
return 1;
}
return 0;
}
/**
* 身份证号的基本合法性校验。15位保证全是数字,18位保证除最后一位是大写X外,全是数字
*
* @return true-验证通过,是身份证号,false-验证不通过,不是身份证号
*/
private boolean basicLegality(String idCardNo) {
if (StringUtils.isEmpty(idCardNo) || (idCardNo.length() != 15 && idCardNo.length() != 18)) { // 不是合格的身份证号,新版18位,旧版15位
return false;
}
String number = idCardNo;
if (idCardNo.length() == 18) { // 新身份证号18位,最后一位可能包含X,单独验证
String lastNumber = idCardNo.substring(17).toUpperCase();
boolean contains = newIdCardNoLastNOSet.contains(lastNumber);
if (!contains) {
return false; // 如果不包含在这里面,那么验证不通过
}
number = number.substring(0, 17);
}
// 做数字验证
for (int i = 0; i < number.length(); i++) {
boolean contains = numberSet.contains(String.valueOf(number.charAt(i)));
if (!contains) {
return false; // 如果不包含在这里面,那么验证不通过
}
}
return true;
}
/**
* 验证新版身份证号,有校验位
* @param newIdCardNo 新版身份证号
* @return true-验证通过,是身份证号,false-验证不通过,不是身份证号
*/
private boolean newIdCardNoValidate(String newIdCardNo) {
// if (StringUtils.isEmpty(newIdCardNo) || newIdCardNo.length() != 18) { // 不是合格的新版身份证号
// return false;
// }
int sum = 0; // 此处用int即可。即使每位都是9,求和之后也远远不会溢出,达到int的最大值
for (int i = 0; i < 17; i++) {
int c = Integer.parseInt(String.valueOf(newIdCardNo.charAt(i)));
sum += weights[i] * c;
}
int checkCodeIndex = sum % 11;
return checkCode[checkCodeIndex].equalsIgnoreCase(newIdCardNo.substring(17));
}
/**
* 15位身份证号码各位的含义:
*1、1-2位省、自治区、直辖市代码;
*2、3-4位地级市、盟、自治州代码
*3、5-6位县、县级市、区代码;
* 4、7-12位出生年月日,比如670401代表1967年4月1日,与18位的第一个区别;
* 5、13-15位为顺序号,其中15位男为单数,女为双数;
*
*
* 验证旧版身份证号,没有校验位,做是否是数字验证
* 验证前6位是否为行政区号
*9-10位小于12
* 11-12位小于31
* @param oldIdCardNo 旧版身份证号
* @return true-验证通过,是身份证号,false-验证不通过,不是身份证号
*/
private boolean oldIdCardNoValidate(String oldIdCardNo) {
boolean flag = false;
// if(StringUtils.isNotEmpty(oldIdCardNo)&& oldIdCardNo.length() == 15){
//
// }
if(areaCodeSet.contains(oldIdCardNo.substring(0,6)) && Integer.parseInt(oldIdCardNo.substring(8,10)) < 13 && Integer.parseInt(oldIdCardNo.substring(10,12)) <32){
flag =true;
}
return flag;
}
public static void main(String[] args) {
System.out.println("值为" + new UdfIsIdcard().evaluate( "130503670241001" ));
}
}