下面我将为您提供一个全面、健壮的 Java 正则表达式解决方案,并详细解释其原理。
核心要点
一个标准的 18 位身份证号由以下部分组成:
- 地址码 (前 6 位): 表示编码对象常住户口所在县(市、旗、区)的行政区划代码,这需要参考国家标准《GB/T 2260》,无法用简单的正则完全覆盖,但我们可以验证它是一个 6 位的数字。
- 出生日期码 (第 7 至 14 位): 格式为
YYYYMMDD,我们需要验证这是一个有效的日期,例如不能是19990230。 - 顺序码 (第 15 至 17 位): 表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,第 17 位奇数分给男性,偶数分给女性。
- 校验码 (第 18 位): 根据前 17 位数字通过特定的算法(ISO 7064:1983, MOD 11-2)计算得出,可能是数字
0-9或字母X。
正则表达式
这个正则表达式分为两部分:基本格式验证和严格的业务逻辑验证。
基本格式正则表达式
这个表达式用于快速验证身份证号的格式是否正确,包括长度、字符类型和简单的日期格式。
^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$
表达式详解:
^: 匹配字符串的开始。[1-9]: 第一位不能是 0。\d{5}: 接下来是 5 位数字,组成 6 位地址码。(18|19|20): 出生年份的前两位,只能是 18, 19, 或 20。\d{2}: 出生年份的后两位。(0[1-9]|1[0-2]): 月份,格式为01-12。(0[1-9]|[12]\d|3[01]): 日期,格式为01-31。(注意:这里不验证闰年,0229也会通过,需要后续代码精确验证)。\d{3}: 顺序码。[\dXx]: 最后一位是数字0-9或字母X(不区分大小写)。- 匹配字符串的结束。
严格业务逻辑验证(推荐)
正则表达式本身无法完成“出生日期是否有效”和“校验码是否正确”这类复杂计算,最佳实践是 “正则 + 逻辑校验” 的组合。
下面是一个完整的 Java 类,它结合了正则表达式和 Java 代码进行严格验证。
完整 Java 代码示例
这是一个封装好的工具类,可以直接使用。
import java.util.Calendar;
import java.util.regex.Pattern;
public class IdCardValidator {
/**
* 严格的 18 位身份证号正则表达式
*/
private static final String ID_CARD_REGEX = "^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dXx]$";
/**
* 验证身份证号是否合法
*
* @param idCard 身份证号
* @return true: 合法, false: 非法
*/
public static boolean isValid(String idCard) {
// 1. 基本格式校验 (正则表达式)
if (idCard == null || !Pattern.matches(ID_CARD_REGEX, idCard)) {
return false;
}
// 2. 出生日期校验 (逻辑)
if (!isDateValid(idCard)) {
return false;
}
// 3. 校验码校验 (逻辑)
return isCheckCodeValid(idCard);
}
/**
* 校验出生日期是否有效
*/
private static boolean isDateValid(String idCard) {
try {
// 提取出生日期部分 (第 7 到 14 位)
String birthDateStr = idCard.substring(6, 14);
int year = Integer.parseInt(birthDateStr.substring(0, 4));
int month = Integer.parseInt(birthDateStr.substring(4, 6));
int day = Integer.parseInt(birthDateStr.substring(6, 8));
// 使用 Calendar 类来验证日期的有效性,可以自动处理闰年等问题
Calendar calendar = Calendar.getInstance();
calendar.setLenient(false); // 设置为非宽松模式,严格校验日期
calendar.set(year, month - 1, day); // 月份是从 0 开始的
// 尝试获取时间,如果日期无效会抛出 IllegalArgumentException
calendar.getTime();
return true;
} catch (Exception e) {
// 日期无效,19990230
return false;
}
}
/**
* 校验校验码是否正确
*/
private static boolean isCheckCodeValid(String idCard) {
// 前17位系数
final int[] WEIGHT = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
// 校验码对应值
final char[] CHECK_CODE = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};
int sum = 0;
for (int i = 0; i < 17; i++) {
sum += (idCard.charAt(i) - '0') * WEIGHT[i];
}
int mod = sum % 11;
char expectedCheckCode = CHECK_CODE[mod];
// 比较计算出的校验码和身份证上的校验码(不区分大小写)
return Character.toUpperCase(idCard.charAt(17)) == expectedCheckCode;
}
// --- 测试代码 ---
public static void main(String[] args) {
// --- 合法身份证号 ---
String validId1 = "11010519900307888X"; // 真实号码已脱敏,格式正确
String validId2 = "440308199901011234"; // 真实号码已脱敏,格式正确
// --- 非法身份证号 ---
String invalidId1 = "123456789012345678"; // 地址码无效
String invalidId2 = "110105199002301234"; // 日期无效 (2月30日)
String invalidId3 = "110105199003078889"; // 校验码错误
String invalidId4 = "11010519900307888"; // 长度不足
String invalidId5 = "X10519900307888X"; // 第一位是X
System.out.println(validId1 + " 是否合法: " + isValid(validId1)); // true
System.out.println(validId2 + " 是否合法: " + isValid(validId2)); // true
System.out.println("-----------------------------------");
System.out.println(invalidId1 + " 是否合法: " + isValid(invalidId1)); // false
System.out.println(invalidId2 + " 是否合法: " + isValid(invalidId2)); // false
System.out.println(invalidId3 + " 是否合法: " + isValid(invalidId3)); // false
System.out.println(invalidId4 + " 是否合法: " + isValid(invalidId4)); // false
System.out.println(invalidId5 + " 是否合法: " + isValid(invalidId5)); // false
}
}
如何选择和使用?
-
快速筛选(前端或后端初步校验): 如果你只需要一个快速的、不涉及复杂计算的格式校验,可以使用
ID_CARD_REGEX这个正则表达式。import java.util.regex.Pattern; // ... String idCard = "11010519900307888X"; boolean isFormatValid = Pattern.matches("^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dXx]$", idCard); -
业务逻辑验证(核心场景): 在需要确保身份证号绝对正确的业务场景下(如用户注册、信息录入),强烈推荐使用上面提供的
IdCardValidator工具类,它结合了正则和逻辑校验,是最可靠、最健壮的方案。
| 验证方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
简单正则 \\d{18} |
极其简单 | 几乎无效,任何18位数字或X都能通过 | 不推荐使用 |
| 严格正则 | 速度快,能验证大部分格式 | 无法验证真实日期和校验码(如 19990230 会通过) |
快速格式筛选,前端初步校验 |
| 正则 + 逻辑校验 | 最准确、最健壮,能验证所有规则 | 代码稍复杂,性能略低于纯正则 | 后端业务逻辑校验,数据入库前验证 |
对于 Java 正则 + 逻辑校验 是验证身份证号的黄金标准。
