mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
fix code
This commit is contained in:
parent
a22f4d686a
commit
68e0186ca7
@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (c) 2024. looly(loolly@aliyun.com)
|
||||
* Hutool is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* https://license.coscl.org.cn/MulanPSL2
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.core.data;
|
||||
|
||||
import org.dromara.hutool.core.regex.ReUtil;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 10位公民身份号码(Citizen Identification Number),一般用于台湾、澳门、香港
|
||||
*
|
||||
* @author Looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class CIN10 {
|
||||
|
||||
private static final Pattern PATTERN_TW = Pattern.compile("^[a-zA-Z][0-9]{9}$");
|
||||
private static final Pattern PATTERN_MC = Pattern.compile("^[157][0-9]{6}\\(?[0-9A-Z]\\)?$");
|
||||
private static final Pattern PATTERN_HK = Pattern.compile("^[A-Z]{1,2}[0-9]{6}\\(?[0-9A]\\)?$");
|
||||
/**
|
||||
* 台湾身份首字母对应数字
|
||||
*/
|
||||
private static final Map<Character, Integer> TW_FIRST_CODE = new HashMap<>();
|
||||
|
||||
static {
|
||||
TW_FIRST_CODE.put('A', 10);
|
||||
TW_FIRST_CODE.put('B', 11);
|
||||
TW_FIRST_CODE.put('C', 12);
|
||||
TW_FIRST_CODE.put('D', 13);
|
||||
TW_FIRST_CODE.put('E', 14);
|
||||
TW_FIRST_CODE.put('F', 15);
|
||||
TW_FIRST_CODE.put('G', 16);
|
||||
TW_FIRST_CODE.put('H', 17);
|
||||
TW_FIRST_CODE.put('J', 18);
|
||||
TW_FIRST_CODE.put('K', 19);
|
||||
TW_FIRST_CODE.put('L', 20);
|
||||
TW_FIRST_CODE.put('M', 21);
|
||||
TW_FIRST_CODE.put('N', 22);
|
||||
TW_FIRST_CODE.put('P', 23);
|
||||
TW_FIRST_CODE.put('Q', 24);
|
||||
TW_FIRST_CODE.put('R', 25);
|
||||
TW_FIRST_CODE.put('S', 26);
|
||||
TW_FIRST_CODE.put('T', 27);
|
||||
TW_FIRST_CODE.put('U', 28);
|
||||
TW_FIRST_CODE.put('V', 29);
|
||||
TW_FIRST_CODE.put('X', 30);
|
||||
TW_FIRST_CODE.put('Y', 31);
|
||||
TW_FIRST_CODE.put('W', 32);
|
||||
TW_FIRST_CODE.put('Z', 33);
|
||||
TW_FIRST_CODE.put('I', 34);
|
||||
TW_FIRST_CODE.put('O', 35);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建并验证台湾、澳门、香港身份证号码
|
||||
*
|
||||
* @param code 台湾、澳门、香港身份证号码
|
||||
* @return CIN10
|
||||
*/
|
||||
public static CIN10 of(final String code) {
|
||||
return new CIN10(code);
|
||||
}
|
||||
|
||||
private final String code;
|
||||
private final String province;
|
||||
private final Gender gender;
|
||||
private final boolean verified;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param code 身份证号码
|
||||
* @throws IllegalArgumentException 身份证格式不支持
|
||||
*/
|
||||
public CIN10(final String code) throws IllegalArgumentException {
|
||||
this.code = code;
|
||||
if (StrUtil.isNotBlank(code)) {
|
||||
if (ReUtil.isMatch(PATTERN_TW, code)) { // 台湾
|
||||
this.province = "台湾";
|
||||
final char char2 = code.charAt(1);
|
||||
if ('1' == char2) {
|
||||
this.gender = Gender.MALE;
|
||||
} else if ('2' == char2) {
|
||||
this.gender = Gender.FEMALE;
|
||||
} else {
|
||||
this.gender = Gender.UNKNOWN;
|
||||
}
|
||||
this.verified = verifyTWCard(code);
|
||||
return;
|
||||
} else if (ReUtil.isMatch(PATTERN_MC, code)) { // 澳门
|
||||
this.province = "澳门";
|
||||
this.gender = Gender.UNKNOWN;
|
||||
this.verified = true;
|
||||
return;
|
||||
} else if (ReUtil.isMatch(PATTERN_HK, code)) { // 香港
|
||||
this.province = "香港";
|
||||
this.gender = Gender.UNKNOWN;
|
||||
this.verified = verfyHKCard(code);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid CIN10 code!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取CIN10码
|
||||
*
|
||||
* @return CIN10码
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取省份
|
||||
*
|
||||
* @return 省份
|
||||
*/
|
||||
public String getProvince() {
|
||||
return province;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取性别
|
||||
*
|
||||
* @return 性别
|
||||
*/
|
||||
public Gender getGender() {
|
||||
return gender;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否验证通过
|
||||
*
|
||||
* @return 是否验证通过
|
||||
*/
|
||||
public boolean isVerified() {
|
||||
return verified;
|
||||
}
|
||||
|
||||
/**
|
||||
* 性别枚举
|
||||
*/
|
||||
public enum Gender {
|
||||
/**
|
||||
* 男
|
||||
*/
|
||||
MALE,
|
||||
/**
|
||||
* 女
|
||||
*/
|
||||
FEMALE,
|
||||
/**
|
||||
* 未知
|
||||
*/
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证台湾身份证号码
|
||||
*
|
||||
* @param code 身份证号码
|
||||
* @return 验证码是否符合
|
||||
*/
|
||||
private static boolean verifyTWCard(final String code) {
|
||||
final Integer iStart = TW_FIRST_CODE.get(code.charAt(0));
|
||||
if (null == iStart) {
|
||||
return false;
|
||||
}
|
||||
int sum = iStart / 10 + (iStart % 10) * 9;
|
||||
|
||||
final String mid = code.substring(1, 9);
|
||||
final char[] chars = mid.toCharArray();
|
||||
int iflag = 8;
|
||||
for (final char c : chars) {
|
||||
sum += Integer.parseInt(String.valueOf(c)) * iflag;
|
||||
iflag--;
|
||||
}
|
||||
|
||||
final String end = code.substring(9, 10);
|
||||
return (sum % 10 == 0 ? 0 : (10 - sum % 10)) == Integer.parseInt(end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证香港身份证号码(存在Bug,部份特殊身份证无法检查)
|
||||
* <p>
|
||||
* 身份证前2位为英文字符,如果只出现一个英文字符则表示第一位是空格,对应数字58 前2位英文字符A-Z分别对应数字10-35 最后一位校验码为0-9的数字加上字符"A","A"代表10
|
||||
* </p>
|
||||
* <p>
|
||||
* 将身份证号码全部转换为数字,分别对应乘9-1相加的总和,整除11则证件号码有效
|
||||
* </p>
|
||||
*
|
||||
* @param code 身份证号码
|
||||
* @return 验证码是否符合
|
||||
*/
|
||||
private static boolean verfyHKCard(final String code) {
|
||||
String card = code.replaceAll("[()]", "");
|
||||
int sum;
|
||||
if (card.length() == 9) {
|
||||
sum = (Character.toUpperCase(card.charAt(0)) - 55) * 9 + (Character.toUpperCase(card.charAt(1)) - 55) * 8;
|
||||
card = card.substring(1, 9);
|
||||
} else {
|
||||
sum = 522 + (Character.toUpperCase(card.charAt(0)) - 55) * 8;
|
||||
}
|
||||
|
||||
// 首字母A-Z,A表示1,以此类推
|
||||
final String mid = card.substring(1, 7);
|
||||
final String end = card.substring(7, 8);
|
||||
final char[] chars = mid.toCharArray();
|
||||
int iflag = 7;
|
||||
for (final char c : chars) {
|
||||
sum = sum + Integer.parseInt(String.valueOf(c)) * iflag;
|
||||
iflag--;
|
||||
}
|
||||
if ("A".equalsIgnoreCase(end)) {
|
||||
sum += 10;
|
||||
} else {
|
||||
sum += Integer.parseInt(end);
|
||||
}
|
||||
return sum % 11 == 0;
|
||||
}
|
||||
}
|
@ -17,8 +17,6 @@ import org.dromara.hutool.core.date.DateUtil;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 身份证相关工具类,参考标准:GB 11643-1999<br>
|
||||
@ -34,39 +32,6 @@ import java.util.Map;
|
||||
*/
|
||||
public class IdcardUtil {
|
||||
|
||||
/**
|
||||
* 台湾身份首字母对应数字
|
||||
*/
|
||||
private static final Map<Character, Integer> TW_FIRST_CODE = new HashMap<>();
|
||||
static {
|
||||
TW_FIRST_CODE.put('A', 10);
|
||||
TW_FIRST_CODE.put('B', 11);
|
||||
TW_FIRST_CODE.put('C', 12);
|
||||
TW_FIRST_CODE.put('D', 13);
|
||||
TW_FIRST_CODE.put('E', 14);
|
||||
TW_FIRST_CODE.put('F', 15);
|
||||
TW_FIRST_CODE.put('G', 16);
|
||||
TW_FIRST_CODE.put('H', 17);
|
||||
TW_FIRST_CODE.put('J', 18);
|
||||
TW_FIRST_CODE.put('K', 19);
|
||||
TW_FIRST_CODE.put('L', 20);
|
||||
TW_FIRST_CODE.put('M', 21);
|
||||
TW_FIRST_CODE.put('N', 22);
|
||||
TW_FIRST_CODE.put('P', 23);
|
||||
TW_FIRST_CODE.put('Q', 24);
|
||||
TW_FIRST_CODE.put('R', 25);
|
||||
TW_FIRST_CODE.put('S', 26);
|
||||
TW_FIRST_CODE.put('T', 27);
|
||||
TW_FIRST_CODE.put('U', 28);
|
||||
TW_FIRST_CODE.put('V', 29);
|
||||
TW_FIRST_CODE.put('X', 30);
|
||||
TW_FIRST_CODE.put('Y', 31);
|
||||
TW_FIRST_CODE.put('W', 32);
|
||||
TW_FIRST_CODE.put('Z', 33);
|
||||
TW_FIRST_CODE.put('I', 34);
|
||||
TW_FIRST_CODE.put('O', 35);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将15位身份证号码转换为18位<br>
|
||||
* 15位身份证号码遵循GB 11643-1989标准。
|
||||
@ -112,8 +77,7 @@ public class IdcardUtil {
|
||||
return false;
|
||||
}
|
||||
case 10: {// 10位身份证,港澳台地区
|
||||
final String[] cardVal = isValidCard10(idCard);
|
||||
return null != cardVal && "true".equals(cardVal[2]);
|
||||
return isValidCard10(idCard);
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
@ -208,122 +172,16 @@ public class IdcardUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证10位身份编码是否合法
|
||||
*
|
||||
* @param idcard 身份编码
|
||||
* @return 身份证信息数组
|
||||
* <p>
|
||||
* [0] - 台湾、澳门、香港 [1] - 性别(男M,女F,未知N) [2] - 是否合法(合法true,不合法false) 若不是身份证件号码则返回null
|
||||
* </p>
|
||||
* 是否有效的10位身份证号码,一般用于判断和验证台湾、澳门、香港身份证
|
||||
* @param idcard 台湾、澳门、香港身份证号码
|
||||
* @return 是否有效的10位身份证号码
|
||||
*/
|
||||
public static String[] isValidCard10(final String idcard) {
|
||||
if (StrUtil.isBlank(idcard)) {
|
||||
return null;
|
||||
}
|
||||
final String[] info = new String[3];
|
||||
final String card = idcard.replaceAll("[()]", "");
|
||||
if (card.length() != 8 && card.length() != 9 && idcard.length() != 10) {
|
||||
return null;
|
||||
}
|
||||
if (idcard.matches("^[a-zA-Z][0-9]{9}$")) { // 台湾
|
||||
info[0] = "台湾";
|
||||
final char char2 = idcard.charAt(1);
|
||||
if ('1' == char2) {
|
||||
info[1] = "M";
|
||||
} else if ('2' == char2) {
|
||||
info[1] = "F";
|
||||
} else {
|
||||
info[1] = "N";
|
||||
info[2] = "false";
|
||||
return info;
|
||||
}
|
||||
info[2] = isValidTWCard(idcard) ? "true" : "false";
|
||||
} else if (idcard.matches("^[157][0-9]{6}\\(?[0-9A-Z]\\)?$")) { // 澳门
|
||||
info[0] = "澳门";
|
||||
info[1] = "N";
|
||||
info[2] = "true";
|
||||
} else if (idcard.matches("^[A-Z]{1,2}[0-9]{6}\\(?[0-9A]\\)?$")) { // 香港
|
||||
info[0] = "香港";
|
||||
info[1] = "N";
|
||||
info[2] = isValidHKCard(idcard) ? "true" : "false";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证台湾身份证号码
|
||||
*
|
||||
* @param idcard 身份证号码
|
||||
* @return 验证码是否符合
|
||||
*/
|
||||
public static boolean isValidTWCard(final String idcard) {
|
||||
if (null == idcard || idcard.length() != 10) {
|
||||
public static boolean isValidCard10(final String idcard){
|
||||
try{
|
||||
return CIN10.of(idcard).isVerified();
|
||||
}catch (final IllegalArgumentException e){
|
||||
return false;
|
||||
}
|
||||
final Integer iStart = TW_FIRST_CODE.get(idcard.charAt(0));
|
||||
if (null == iStart) {
|
||||
return false;
|
||||
}
|
||||
int sum = iStart / 10 + (iStart % 10) * 9;
|
||||
|
||||
final String mid = idcard.substring(1, 9);
|
||||
final char[] chars = mid.toCharArray();
|
||||
int iflag = 8;
|
||||
for (final char c : chars) {
|
||||
sum += Integer.parseInt(String.valueOf(c)) * iflag;
|
||||
iflag--;
|
||||
}
|
||||
|
||||
final String end = idcard.substring(9, 10);
|
||||
return (sum % 10 == 0 ? 0 : (10 - sum % 10)) == Integer.parseInt(end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证香港身份证号码(存在Bug,部份特殊身份证无法检查)
|
||||
* <p>
|
||||
* 身份证前2位为英文字符,如果只出现一个英文字符则表示第一位是空格,对应数字58 前2位英文字符A-Z分别对应数字10-35 最后一位校验码为0-9的数字加上字符"A","A"代表10
|
||||
* </p>
|
||||
* <p>
|
||||
* 将身份证号码全部转换为数字,分别对应乘9-1相加的总和,整除11则证件号码有效
|
||||
* </p>
|
||||
*
|
||||
* @param idcard 身份证号码
|
||||
* @return 验证码是否符合
|
||||
*/
|
||||
public static boolean isValidHKCard(final String idcard) {
|
||||
if (StrUtil.isBlank(idcard)) {
|
||||
return false;
|
||||
}
|
||||
if(false == idcard.matches("^[A-Z]{1,2}[0-9]{6}\\(?[0-9A]\\)?$")){
|
||||
return false;
|
||||
}
|
||||
|
||||
String card = idcard.replaceAll("[()]", "");
|
||||
int sum;
|
||||
if (card.length() == 9) {
|
||||
sum = (Character.toUpperCase(card.charAt(0)) - 55) * 9 + (Character.toUpperCase(card.charAt(1)) - 55) * 8;
|
||||
card = card.substring(1, 9);
|
||||
} else {
|
||||
sum = 522 + (Character.toUpperCase(card.charAt(0)) - 55) * 8;
|
||||
}
|
||||
|
||||
// 首字母A-Z,A表示1,以此类推
|
||||
final String mid = card.substring(1, 7);
|
||||
final String end = card.substring(7, 8);
|
||||
final char[] chars = mid.toCharArray();
|
||||
int iflag = 7;
|
||||
for (final char c : chars) {
|
||||
sum = sum + Integer.parseInt(String.valueOf(c)) * iflag;
|
||||
iflag--;
|
||||
}
|
||||
if ("A".equalsIgnoreCase(end)) {
|
||||
sum += 10;
|
||||
} else {
|
||||
sum += Integer.parseInt(end);
|
||||
}
|
||||
return sum % 11 == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -163,20 +163,20 @@ public class IdcardUtilTest {
|
||||
@Test
|
||||
public void isValidHKCardIdTest(){
|
||||
final String hkCard="P174468(6)";
|
||||
final boolean flag=IdcardUtil.isValidHKCard(hkCard);
|
||||
final boolean flag=IdcardUtil.isValidCard10(hkCard);
|
||||
assertTrue(flag);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isValidTWCardIdTest() {
|
||||
final String twCard = "B221690311";
|
||||
boolean flag = IdcardUtil.isValidTWCard(twCard);
|
||||
boolean flag = IdcardUtil.isValidCard10(twCard);
|
||||
assertTrue(flag);
|
||||
final String errTwCard1 = "M517086311";
|
||||
flag = IdcardUtil.isValidTWCard(errTwCard1);
|
||||
flag = IdcardUtil.isValidCard10(errTwCard1);
|
||||
assertFalse(flag);
|
||||
final String errTwCard2 = "B2216903112";
|
||||
flag = IdcardUtil.isValidTWCard(errTwCard2);
|
||||
flag = IdcardUtil.isValidCard10(errTwCard2);
|
||||
assertFalse(flag);
|
||||
}
|
||||
|
||||
@ -196,8 +196,7 @@ public class IdcardUtilTest {
|
||||
@Test
|
||||
public void issueIAFOLITest() {
|
||||
final String idcard = "H01487002";
|
||||
assertFalse(IdcardUtil.isValidHKCard(idcard));
|
||||
assertNull(IdcardUtil.isValidCard10(idcard));
|
||||
assertFalse(IdcardUtil.isValidCard10(idcard));
|
||||
assertFalse(IdcardUtil.isValidCard(idcard));
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user