大陆居民身份证15位转18位
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>身份证号码15位转18位
* 判断15位身份证是否合法,合法进行身份证转换,返回18位身份证号,非法返回传入的身份证参数
* 合法性判定规则:
* (1)前6位是省份和城市的行政区划代码,6位数字且首位非0
* (2)第7-12位出生年月日,如670401代表1967年4月1日
* (3)第13-15位为顺序号,其中15位男为单数,女为双数。
* 身份证转换步骤:
* 1.在原15位数身份证的第6位数后面插入19
* 2.按照国家规定的统一公式计算出第十八位数,作为校验码放在第二代身份证的尾号
* 生成校验位规则同18位身份证校验码校验步骤
* </p>
*
* @author 谭黄
* @since 2023/11/1
*/
@org.apache.hadoop.hive.ql.exec.Description(name = "IdNumber15ConvertTo18",
extended = "示例:select IdNumber15ConvertTo18(name) from src;",
value = "_FUNC_(col)-将校验col字段是否为合法15位身份证号,合法返回转换的18位身份证号,非法返回入参")
public class IdNumber15ConvertTo18 extends UDF {
/**
* 验证15位身份证号码的正则,几乎是全数字 15位全数字,首位不为0
*/
private static final String REGEX = "^[1-9][0-9]{5}\\d{6}\\d{3}$";
/**
* 正则预编译
*/
private static final Pattern PATTERN = Pattern.compile(REGEX);
/**
* 函数的执行逻辑
* @param s
* @return
*/
public Text evaluate(final Text s) {
if (s == null) {
return null;
}
String text = s.toString();
//不是15位
if (!(text.length() == 15)) {
return new Text(text);
}
Matcher matcher = PATTERN.matcher(text);
//正则校验
if (!matcher.matches()) {
return new Text(text);
}
String dateStr = text.substring(6, 12);
//验证日期段
if(!isValidDate(dateStr)){
return new Text(text);
}
//插入19年,得到17位身份证号
String text17 = new StringBuilder(text).insert(6,"19").toString();
//计算校验码
char c = calculateChekeCode(text17);
return new Text(text17+c);
}
/**
* 根据前17位计算校验码,也就是第18位
*
* @param text17
* @return
*/
private char 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;
}
return VALUE_MAPPING.get(sum % 11);
}
/**
* 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 yyMMdd
* @return
*/
private static boolean isValidDate(String dateStr) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyMMdd");
dateFormat.setLenient(false); // 关闭宽松模式
try {
// 尝试解析日期字符串
Date date = dateFormat.parse(dateStr);
// 如果解析成功,且日期没有被解释为宽松模式,则认为是有效日期
return !dateFormat.isLenient();
} catch (ParseException e) {
// 解析失败,不是有效日期
return false;
}
}
}