杰瑞科技汇

Java身份证号正则表达式如何写?

下面我将为您提供一个全面、健壮的 Java 正则表达式解决方案,并详细解释其原理。

核心要点

一个标准的 18 位身份证号由以下部分组成:

  1. 地址码 (前 6 位): 表示编码对象常住户口所在县(市、旗、区)的行政区划代码,这需要参考国家标准《GB/T 2260》,无法用简单的正则完全覆盖,但我们可以验证它是一个 6 位的数字。
  2. 出生日期码 (第 7 至 14 位): 格式为 YYYYMMDD,我们需要验证这是一个有效的日期,例如不能是 19990230
  3. 顺序码 (第 15 至 17 位): 表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,第 17 位奇数分给男性,偶数分给女性。
  4. 校验码 (第 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
    }
}

如何选择和使用?

  1. 快速筛选(前端或后端初步校验): 如果你只需要一个快速的、不涉及复杂计算的格式校验,可以使用 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);
  2. 业务逻辑验证(核心场景): 在需要确保身份证号绝对正确的业务场景下(如用户注册、信息录入),强烈推荐使用上面提供的 IdCardValidator 工具类,它结合了正则和逻辑校验,是最可靠、最健壮的方案。

验证方式 优点 缺点 适用场景
简单正则 \\d{18} 极其简单 几乎无效,任何18位数字或X都能通过 不推荐使用
严格正则 速度快,能验证大部分格式 无法验证真实日期和校验码(如 19990230 会通过) 快速格式筛选,前端初步校验
正则 + 逻辑校验 最准确、最健壮,能验证所有规则 代码稍复杂,性能略低于纯正则 后端业务逻辑校验,数据入库前验证

对于 Java 正则 + 逻辑校验 是验证身份证号的黄金标准。

分享:
扫描分享到社交APP
上一篇
下一篇