大陆居民身份证18位身份证校验位校验udf关键代码
package com.audaque.udf;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <p>身份证号符合性校验
* 判断18位身份证是否合法,合法返回0,非法返回非0
* 合法性判定规则:
* (1):前6位是省份和城市的行政区划代码,6位数字且首位非0
* (2):第7到14位是出生日期,依次表示年、月、日
* (3):第15到17位是顺序码,表示在同一天出生的人的顺序编号
* (4):最后一位是校验码,用于检验身份证号码的正确性
* 校验码校验步骤:
* 1.将身份证号码的前17位依次乘以对应的权重值:第1位乘以7,第2位乘以9,第3位乘以10,依此类推,第17位乘以3。
* 2.将上述结果相加得到总和。
* 3.将总和除以11,得到的余数即为校验码。
* 4.根据余数对应的规则,得到校验码的具体值。
* 根据规则,余数为0时,校验码为1;余数为1时,校验码为0;余数为2到10时,校验码由相应的规则确定。
* </p>
*
* @author 谭黄
* @since 2023/10/31
*/
@org.apache.hadoop.hive.ql.exec.Description(name = "IdNumberComplianceVerification",
extended = "示例:select IdNumberComplianceVerification(name) from src;",
value = "_FUNC_(col)-将校验col字段是否为合法18位身份证号,合法返回0,非法返回非0")
public class IdNumberComplianceVerification extends UDF {
/**
* 验证前17位的正则
* 首位不为0,5位数字,8位数字,3位数字
*/
private static final String REGEX = "^[1-9][0-9]{5}\\d{8}\\d{3}$";
/**
* 正则预编译
*/
private static final Pattern PATTERN = Pattern.compile(REGEX);
/**
* 通过的返回值
*/
private static final String YES = "0";
/**
* 未校验通过的返回值
*/
private static final String NO = "1";
/**
* 函数的执行逻辑
* @param s
* @return
*/
public Text evaluate(final Text s) {
if (s == null) {
return null;
}
String text = s.toString();
//不是18位
if (!(text.length() == 18)) {
return new Text(NO);
}
String text17 = text.substring(0, 17);
Matcher matcher = PATTERN.matcher(text17);
//验证前17位正则
if (!matcher.matches()) {
return new Text(NO);
}
String dateStr = text.substring(6, 14);
//验证日期段
if(!isValidDate(dateStr)){
return new Text(NO);
}
//计算校验码
int value18 = calculateChekeCode(text17);
int text18 = Character.toUpperCase(text.charAt(17));
//验证校验码
if(value18==text18){
return new Text(YES);
}
return new Text(NO);
}
/**
* 根据前17位计算校验码,也就是第18位
*
* @param text17
* @return
*/
private int calculateChekeCode(String text17) {
char[] charArray = text17.toCharArray();
List<Integer> nums = new ArrayList<>();
for (char c : charArray) {
nums.add( (int)c - (int)('0'));
}
int sum = 0;
for (int i = 0; i < nums.size(); i++) {
int value = nums.get(i) * COEFFICIENTS.get(i);
sum += value;
}
int value18 = VALUE_MAPPING.get(sum % 11);
return value18;
}
/**
* 17位系数,用下标对应
*/
private final static List<Integer> COEFFICIENTS;
/**
* 余数和校验码的映射关系
*/
private final static Map<Integer,Character> VALUE_MAPPING;
static {
Map<Integer,Character> map = new HashMap<>(16);
map.put(0,'1');
map.put(1,'0');
map.put(2,'X');
map.put(3,'9');
map.put(4,'8');
map.put(5,'7');
map.put(6,'6');
map.put(7,'5');
map.put(8,'4');
map.put(9,'3');
map.put(10,'2');
VALUE_MAPPING=Collections.unmodifiableMap(map);
}
static {
List<Integer> list = new ArrayList<>(17);
list.add(7);
list.add(9);
list.add(10);
list.add(5);
list.add(8);
list.add(4);
list.add(2);
list.add(1);
list.add(6);
list.add(3);
list.add(7);
list.add(9);
list.add(10);
list.add(5);
list.add(8);
list.add(4);
list.add(2);
COEFFICIENTS= Collections.unmodifiableList(list);
}
/**
* 日期校验
* @param dateStr yyyyMMdd
* @return
*/
private static boolean isValidDate(String dateStr) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
dateFormat.setLenient(false); // 关闭宽松模式
try {
// 尝试解析日期字符串
Date date = dateFormat.parse(dateStr);
// 如果解析成功,且日期没有被解释为宽松模式,则认为是有效日期
return !dateFormat.isLenient();
} catch (ParseException e) {
// 解析失败,不是有效日期
return false;
}
}
public static void main(String[] args) {
IdNumberComplianceVerification abc = new IdNumberComplianceVerification();
System.out.println(abc.evaluate(new Text("511024198904211010")));
}
}