首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> JSP 程式設計 >> 關於JSP

Java 實現 18 位身份證號碼校驗碼算法 (GB 11643-1999 標準)

     編輯:關於JSP

中國大陸居民身份證號碼校驗碼計算標準實作

中華人民共和國居民身份證號碼為 18 位,前 17 位為本體碼,第 18 位為校驗碼。校驗碼依據 ISO 7064:1983.MOD 11-2** 標準計算,能有效檢測單位數字錯誤、相鄰數字順序錯誤等常見輸入錯誤。本文提供標準 Java 實作與詳細算法解析。

算法規範 (GB 11643-1999)

  1. 加權因子 (Wi):對應前 17 位的固定權重
    Wi = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
  2. 校驗碼對照表 (Vi):模 11 餘數對應的校驗字元
    餘數:  0  1  2  3  4  5  6  7  8  9  10
    校驗碼: 1  0  X  9  8  7  6  5  4  3  2
  3. 計算公式:
    S = Σ(ai × Wi)  (i=1..17)
    Y = S mod 11
    校驗碼 = V[Y]

標準 Java 實作

public class IdCardValidator {

    // 加權因子陣列 (長度 17)
    private static final int[] WEIGHTS = {
        7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2
    };
    
    // 校驗碼對照表 (索引 = 餘數 0-10)
    private static final char[] CHECK_CODES = {
        '1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'
    };

    /**
     * 計算身份證號碼的校驗碼
     * @param id17 前 17 位數字字串
     * @return 校驗碼字元 ('0'-'9' 或 'X')
     * @throws IllegalArgumentException 輸入非 17 位純數字
     */
    public static char calculateCheckCode(String id17) {
        if (id17 == null || id17.length() != 17 || !id17.matches("\\d{17}")) {
            throw new IllegalArgumentException("身份證本體碼必須為 17 位純數字");
        }
        
        int sum = 0;
        for (int i = 0; i < 17; i++) {
            int digit = id17.charAt(i) - '0';  // char 轉 int
            sum += digit * WEIGHTS[i];
        }
        
        int remainder = sum % 11;
        return CHECK_CODES[remainder];
    }

    /**
     * 驗證完整 18 位身份證號碼是否合法
     * @param id18 18 位身份證號碼
     * @return true 表示校驗碼正確
     */
    public static boolean validate(String id18) {
        if (id18 == null || id18.length() != 18) return false;
        
        String id17 = id18.substring(0, 17);
        char providedCheckCode = Character.toUpperCase(id18.charAt(17));
        char calculatedCheckCode = calculateCheckCode(id17);
        
        return providedCheckCode == calculatedCheckCode;
    }

    /**
     * 完整驗證:格式 + 校驗碼 + 出生日期合法性 + 地址碼存在性
     * @param id18 18 位身份證號碼
     * @return 驗證結果物件
     */
    public static ValidationResult validateFull(String id18) {
        ValidationResult result = new ValidationResult();
        result.setIdCard(id18);
        
        // 1. 基本格式
        if (id18 == null || id18.length() != 18) {
            result.addError("長度必須為 18 位");
            return result;
        }
        if (!id18.substring(0, 17).matches("\\d{17}")) {
            result.addError("前 17 位必須為數字");
            return result;
        }
        char lastChar = Character.toUpperCase(id18.charAt(17));
        if (!"0123456789X".contains(String.valueOf(lastChar))) {
            result.addError("第 18 位必須為數字或 X");
            return result;
        }
        
        // 2. 校驗碼
        if (!validate(id18)) {
            result.addError("校驗碼不正確");
            return result;
        }
        
        // 3. 出生日期合法性 (第 7-14 位: YYYYMMDD)
        String birthStr = id18.substring(6, 14);
        try {
            LocalDate birth = LocalDate.parse(birthStr, DateTimeFormatter.BASIC_ISO_DATE);
            LocalDate now = LocalDate.now();
            if (birth.isAfter(now) || birth.isBefore(LocalDate.of(1900, 1, 1))) {
                result.addError("出生日期不合理");
            }
            result.setBirthDate(birth);
        } catch (DateTimeParseException e) {
            result.addError("出生日期格式無效");
        }
        
        // 4. 地址碼 (前 6 位) - 這裡僅做基本格式檢查,完整版需查詢行政區劃碼庫
        String addressCode = id18.substring(0, 6);
        if (!addressCode.matches("\\d{6}")) {
            result.addError("地址碼格式錯誤");
        }
        result.setAddressCode(addressCode);
        
        // 5. 性別 (第 17 位: 奇數男、偶數女)
        int genderDigit = id18.charAt(16) - '0';
        result.setGender(genderDigit % 2 == 1 ? "男" : "女");
        
        result.setValid(result.getErrors().isEmpty());
        return result;
    }

    // 內部結果類
    public static class ValidationResult {
        private String idCard;
        private boolean valid;
        private List errors = new ArrayList<>();
        private LocalDate birthDate;
        private String addressCode;
        private String gender;
        // Getters/Setters 省略...
    }

    // 測試主程式
    public static void main(String[] args) {
        String[] testCases = {
            "11010519900307783X",  // 正確範例
            "110105199003077839",  // 校驗碼錯誤
            "11010519900307783",   // 長度錯誤
            "320124198506150025"   // 另一正確範例
        };
        
        for (String id : testCases) {
            ValidationResult r = validateFull(id);
            System.out.printf("%s => %s %s%n", id, r.isValid() ? "✅ 合法" : "❌ 不合法", r.getErrors());
        }
    }
}

算法原理深度解析

為何選用 MOD 11-2?

  • 錯誤檢測能力強:能檢測 100% 的單位數字錯誤、99.9% 的相鄰數字順序錯誤、99.9% 的雙位數字錯誤
  • 校驗碼包含 'X':模 11 餘數範圍 0-10 (11 種),需 11 個字元表示,引入 'X' 代表 10
  • 國際標準**:ISO 7064:1983 規範,同用於 ISBN-10、銀行卡等

加權因子設計邏輯

權重陣列 `[7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2]` 並非隨機,而是基於數學優化:

  • 權重互質於 11,確保模 11 運算分布均勻
  • 權重變化大,相鄰位置權重差異明顯,增強順序錯誤檢測
  • 前後半段權重鏡像對稱,便於硬體電路實現

生產環境擴展建議

功能實作方向
行政區劃碼驗證引入最新 `GB/T 2260` 行政區劃代碼庫 (定期更新)
15 位舊版身份證支援轉換邏輯:出生年補 19/20、重新計算校驗碼
港澳台居民身份證不同格式規則,需獨立驗證器
高併發效能預計算權重表、避免物件創建、使用基礎型別運算
單元測試覆蓋邊界值 (1900/2100 年)、閏年、無效日期、所有校驗碼值

常見誤區提醒

  • ❌ 以為校驗碼通過 = 身份證真實 (僅代表格式合法,非真偽鑑別)
  • ❌ 忽略大小寫:校驗碼 'x' 和 'X' 皆合法,需統一轉大寫比對
  • ❌ 不驗證出生日期合法性 (如 19900230、20250101)
  • ❌ 硬編碼權重/校驗表字串拼接,難以維護測試

身份證校驗是表單驗證、實名認證、風控系統的基礎關卡。掌握標準算法並建立可複用、可測試、可擴展的驗證組件,是 Java 後端開發的基本功之一。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved