身份證號(hào)碼的編碼規(guī)則及校驗(yàn)

前言

根據(jù)最新修訂的《中華人民共和國(guó)居民身份證法》第二十三條規(guī)定,依照《中華人民共和國(guó)居民身份證條例》領(lǐng)取的居民身份證,自2013年1月1日起停止使用。即一代身份證已于2013年1月1日起停止使用,本文內(nèi)容適用于二代身份證,如無(wú)特殊說(shuō)明,本文中所說(shuō)的身份證皆指二代身份證。

筆者目前使用的jdk版本是1.6.0_29,Eclipse版本是Juno Release,Build id 20120614-1722。如無(wú)特殊說(shuō)明,本文所有的Java代碼都是基于此。

本文包含大量數(shù)學(xué)公式和Java代碼,手機(jī)端瀏覽體驗(yàn)較差,可在手機(jī)端瀏覽文字性內(nèi)容,數(shù)學(xué)公式和Java代碼在PC端瀏覽,同時(shí)建議準(zhǔn)備好紙、筆用于數(shù)學(xué)公式的演算。具有一定數(shù)學(xué)基礎(chǔ)的讀者也可以忽略此條建議。

本文中提供的Java代碼都是筆者逐行編寫(xiě)、反復(fù)斟酌,以求能夠拋磚引玉,使初學(xué)者可以孜孜不倦,在Java的學(xué)習(xí)道路上更進(jìn)一層樓。本文內(nèi)容,不足之處,歡迎大家批評(píng)指正。

修訂記錄

版本號(hào) 修訂日期 修訂說(shuō)明
V0.1 2018/08/13 初稿
V1.0 2018/09/02 發(fā)布

參考資料

  1. 中華人民共和國(guó)居民身份證法
  2. 中華人民共和國(guó)國(guó)家標(biāo)準(zhǔn) GB/T 2260-2007 中華人民共和國(guó)行政區(qū)劃代碼
  3. 中華人民共和國(guó)國(guó)家標(biāo)準(zhǔn) GB 11643-1999 公民身份證號(hào)碼
  4. 中華人民共和國(guó)國(guó)家標(biāo)準(zhǔn) GB/T 17710-1999 數(shù)據(jù)處理 校驗(yàn)碼系統(tǒng)
  5. 中華人民共和國(guó)國(guó)家標(biāo)準(zhǔn) GB/T 17710-2008 信息技術(shù) 安全技術(shù) 校驗(yàn)字符系統(tǒng)
  6. ISO 7064:1983 Data processing - Check character systems
  7. ISO/IEC 7064:2003 Information technology - Security techniques - Check character systems

身份證號(hào)碼的編碼規(guī)則

身份證號(hào)碼共18位,由17位本體碼和1位校驗(yàn)碼組成。

  1. 前6位是地址碼,表示登記戶口時(shí)所在地的行政區(qū)劃代碼,依照《中華人民共和國(guó)行政區(qū)劃代碼》國(guó)家標(biāo)準(zhǔn)(GB/T2260)的規(guī)定執(zhí)行;
  2. 7到14位是出生年月日,采用YYYYMMDD格式;
  3. 15到17位是順序碼,表示在同一地址碼所標(biāo)識(shí)的區(qū)域范圍內(nèi),對(duì)同年、同月、同日出生的人編訂的順序號(hào),順序碼的奇數(shù)分配給男性,偶數(shù)分配給女性,即第17位奇數(shù)表示男性,偶數(shù)表示女性;
  4. 第18位是校驗(yàn)碼,采用ISO 7064:1983, MOD 11-2校驗(yàn)字符系統(tǒng),計(jì)算規(guī)則下一章節(jié)說(shuō)明。

一代身份證與二代身份證的區(qū)別在于:

  1. 一代身份證是15位,二代身份證是18位;
  2. 一代身份證出生年月日采用YYMMDD格式,二代身份證出生年月日采用YYYYMMDD格式;
  3. 一代身份證無(wú)校驗(yàn)碼,二代身份證有校驗(yàn)碼。

校驗(yàn)碼計(jì)算規(guī)則

身份證號(hào)碼中各個(gè)位置上的號(hào)碼字符值應(yīng)滿足下列公式的校驗(yàn):
\sum_{i=1}^{18} \left ( a_{i} \times W_{i}\right ) \equiv 1 \pmod{11}

  • i表示號(hào)碼字符從右至左包括校驗(yàn)碼字符在內(nèi)的位置序號(hào);
  • a_{i}表示第i位置上的號(hào)碼字符值,a_{1}是身份證號(hào)碼第18位校驗(yàn)碼;
  • W_{i}表示第i位置上的加權(quán)因子,加權(quán)因子計(jì)算公式:W_{i} = 2^{i-1} \pmod{11}

舉例說(shuō)明:

筆者身份證號(hào)碼為370683198901117657,根據(jù)上述公式進(jìn)行校驗(yàn)。

\sum_{i=1}^{18} \left ( a_{i} \times W_{i}\right ) = 397 \equiv 1 \pmod{11}

大家可以根據(jù)此方法驗(yàn)證自己的身份證號(hào)碼。

了解了身份證號(hào)碼的校驗(yàn)公式后,根據(jù)同余定理可證得身份證號(hào)碼校驗(yàn)碼的計(jì)算公式:
\begin{align*} & \because W_{1} = 2^{1-1} \pmod{11} = 1 \pmod{11} = 1 \\ & \therefore a_{1} + \sum_{i=2}^{18} ( a_{i} \times W_{i} )\equiv 1 \pmod{11} \\ & \therefore a_{1} \pmod{11} + \sum_{i=2}^{18} ( a_{i} \times W_{i} ) \pmod{11} \equiv 1 \pmod{11} \\ & \therefore a_{1} \pmod{11} + \sum_{i=2}^{18} ( a_{i} \times W_{i} ) \pmod{11} \equiv 12 \pmod{11} \\ & \therefore a_{1} \pmod{11} \equiv ( 12 - \sum_{i=2}^{18} ( a_{i} \times W_{i} ) \pmod{11} ) \pmod{11} \\ & \because 0 \leqslant a_{1} \leqslant 10 \land a_{1} \in N \\ & \therefore a_{1} = ( 12 - \sum_{i=2}^{18} ( a_{i} \times W_{i} ) \pmod{11} ) \pmod{11} \end{align*}

當(dāng)a_{1}值等于10時(shí),用羅馬數(shù)字符X表示。此處需要注意:是羅馬數(shù)字X,不應(yīng)理解為英文字母X

實(shí)際應(yīng)用

在金融行業(yè)軟件系統(tǒng)中,對(duì)于身份證號(hào)碼的采集、校驗(yàn)用途甚廣。

  1. 身份證號(hào)碼前6位,可采集客戶戶籍所在地,只需將國(guó)家標(biāo)準(zhǔn)GB/T 2260中定義的行政區(qū)劃代碼導(dǎo)入數(shù)據(jù)庫(kù),程序中進(jìn)行映射即可;但需要注意的是,行政區(qū)劃代碼每隔幾年會(huì)修訂一次,從筆者手上的2007版來(lái)看,共經(jīng)歷了1982年、1984年、1986年、1988年、1991年、1995年、1999年、2002年、2007年九次修訂,所以要預(yù)留更新機(jī)制;
  2. 身份證號(hào)碼7到14位,可采集客戶的出生日期、年齡、生日;
  3. 身份證號(hào)碼17位,可采集客戶的性別,奇數(shù)表示男性,偶數(shù)表示女性;
  4. 身份證號(hào)碼的驗(yàn)證渠道,不管驗(yàn)證成功與否,往往都是收費(fèi)的,比如銀行渠道、公安部渠道,在發(fā)往這些渠道驗(yàn)證之前,先對(duì)其進(jìn)行長(zhǎng)度、正則表達(dá)式、校驗(yàn)碼的驗(yàn)證,能夠適當(dāng)提高收費(fèi)驗(yàn)證的成功率,節(jié)省成本支出;而且也可以提升用戶體驗(yàn),在用戶輸入錯(cuò)誤時(shí)及時(shí)反饋而不必等待驗(yàn)證渠道結(jié)果的返回。

下面,就以實(shí)際代碼為例,說(shuō)明身份證號(hào)碼校驗(yàn)的方法。首先,給出身份證號(hào)碼的正則表達(dá)式:

/**
 * 18位二代身份證號(hào)碼的正則表達(dá)式
 */
public static final String REGEX_ID_NO_18 = "^"
        + "\\d{6}" // 6位地區(qū)碼
        + "(18|19|([23]\\d))\\d{2}" // 年YYYY
        + "((0[1-9])|(10|11|12))" // 月MM
        + "(([0-2][1-9])|10|20|30|31)" // 日DD
        + "\\d{3}" // 3位順序碼
        + "[0-9Xx]" // 校驗(yàn)碼
        + "$";

/**
 * 15位一代身份證號(hào)碼的正則表達(dá)式
 */
public static final String REGEX_ID_NO_15 = "^"
        + "\\d{6}" // 6位地區(qū)碼
        + "\\d{2}" // 年YYYY
        + "((0[1-9])|(10|11|12))" // 月MM
        + "(([0-2][1-9])|10|20|30|31)" // 日DD
        + "\\d{3}"http:// 3位順序碼
        + "$";

校驗(yàn)身份證號(hào)碼:

/**
 * 校驗(yàn)身份證號(hào)碼
 * 
 * <p>
 * 適用于18位的二代身份證號(hào)碼
 * </p>
 * 
 * @param IDNo18 身份證號(hào)碼
 * @return true - 校驗(yàn)通過(guò)<br>
 *         false - 校驗(yàn)不通過(guò)
 * @throws IllegalArgumentException 
 *             如果身份證號(hào)碼為空或長(zhǎng)度不為18位或不滿足身份證號(hào)碼組成規(guī)則
 *             <i>6位地址碼+
 *             出生年月日YYYYMMDD+3位順序碼
 *             +0~9或X(x)校驗(yàn)碼</i>
 */
public static boolean checkIDNo(String IDNo18) {
    // 校驗(yàn)身份證號(hào)碼的長(zhǎng)度
    if (!checkStrLength(IDNo18, 18)) {
        throw new IllegalArgumentException();
    }
    // 匹配身份證號(hào)碼的正則表達(dá)式
    if (!regexMatch(IDNo18, REGEX_ID_NO_18)) {
        throw new IllegalArgumentException();
    }
    // 校驗(yàn)身份證號(hào)碼的驗(yàn)證碼
    return validateCheckNumber(IDNo18);
}

/**
 * 校驗(yàn)字符串長(zhǎng)度
 * 
 * @param inputString 字符串
 * @param len 預(yù)期長(zhǎng)度
 * @return true - 校驗(yàn)通過(guò)<br>
 *         false - 校驗(yàn)不通過(guò)
 */
private static boolean checkStrLength(String inputString, int len) {
    if (inputString == null || inputString.length() != len) {
        return false;
    }
    return true;
}

/**
 * 匹配正則表達(dá)式
 * 
 * @param inputString 字符串
 * @param regex 正則表達(dá)式
 * @return true - 校驗(yàn)通過(guò)<br>
 *         false - 校驗(yàn)不通過(guò)
 */
private static boolean regexMatch(String inputString, String regex) {
    return inputString.matches(regex);
}

/**
 * 校驗(yàn)碼校驗(yàn)
 * <p>
 * 適用于18位的二代身份證號(hào)碼
 * </p>
 * 
 * @param IDNo18 身份證號(hào)碼
 * @return true - 校驗(yàn)通過(guò)<br>
 *         false - 校驗(yàn)不通過(guò)
 */
private static boolean validateCheckNumber(String IDNo18) {
    // 加權(quán)因子
    int[] W = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
    char[] IDNoArray = IDNo18.toCharArray();
    int sum = 0;
    for (int i = 0; i < W.length; i++) {
        sum += Integer.parseInt(String.valueOf(IDNoArray[i])) * W[i];
    }
    // 校驗(yàn)位是X,則表示10
    if (IDNoArray[17] == 'X' || IDNoArray[17] == 'x') {
        sum += 10;
    } else {
        sum += Integer.parseInt(String.valueOf(IDNoArray[17]));
    }
    // 如果除11模1,則校驗(yàn)通過(guò)
    return sum % 11 == 1;
}

計(jì)算校驗(yàn)碼:

/**
 * 計(jì)算身份證號(hào)碼的校驗(yàn)碼
 * <p>
 * 適用于18位的二代身份證號(hào)碼,身份證號(hào)碼由17位本體碼和1位校驗(yàn)碼組成
 * </p>
 * 
 * @param masterNumber 本體碼
 * @return 身份證號(hào)碼
 * @throws IllegalArgumentException 
 *             如果本體碼為空或長(zhǎng)度不為17位或不滿足本體碼組成規(guī)則
 *             <i>6位地址碼+
 *             出生年月日YYYYMMDD+3位順序碼</i>
 */
public static String computeIDNoCheckNumber(String masterNumber) {
    // 校驗(yàn)本體碼的長(zhǎng)度
    if (!checkStrLength(masterNumber, 17)) {
        throw new IllegalArgumentException();
    }
    // 匹配本體碼的正則表達(dá)式
    if (!regexMatch(masterNumber, REGEX_MASTER_NUMBER)) {
        throw new IllegalArgumentException();
    }
    // 計(jì)算校驗(yàn)碼
    String checkNumber = computeCheckNumber(masterNumber);
    // 返回本體碼+校驗(yàn)碼=完整的身份證號(hào)碼
    return masterNumber + checkNumber;
}

/**
 * 計(jì)算校驗(yàn)碼
 * <p>
 * 適用于18位的二代身份證號(hào)碼
 * </p>
 * 
 * @param masterNumber 本體碼
 * @return 校驗(yàn)碼
 */
private static String computeCheckNumber(String masterNumber) {
    // 加權(quán)因子
    int[] W = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
    char[] masterNumberArray = masterNumber.toCharArray();
    int sum = 0;
    for (int i = 0; i < W.length; i++) {
        sum += Integer.parseInt(String.valueOf(masterNumberArray[i])) * W[i];
    }
    // 根據(jù)同余定理得到的校驗(yàn)碼數(shù)組
    String[] checkNumberArray = { "1", "0", "X", "9", "8", "7", "6", "5", "4",
            "3", "2" };
    // 得到校驗(yàn)碼
    String checkNumber = checkNumberArray[sum % 11];
    // 返回校驗(yàn)碼
    return checkNumber;
}

雖然15位的一代身份證已經(jīng)停用,但是難免有需要用到將15位的一代身份證升級(jí)為18位的二代身份證的情形,代碼示例如下:

/**
 * 15位一代身份證號(hào)碼升級(jí)18位二代身份證號(hào)碼
 * <p>
 * 為15位的一代身份證號(hào)碼增加年份的前2位和最后1位校驗(yàn)碼
 * </p>
 * 
 * @param IDNo15 15位的一代身份證號(hào)碼
 * @return 18位的二代身份證號(hào)碼
 */
public static String updateIDNo15to18(String IDNo15) {
    // 校驗(yàn)身份證號(hào)碼的長(zhǎng)度
    if (!checkStrLength(IDNo15, 15)) {
        throw new IllegalArgumentException();
    }
    // 匹配身份證號(hào)碼的正則表達(dá)式
    if (!regexMatch(IDNo15, REGEX_ID_NO_15)) {
        throw new IllegalArgumentException();
    }
    // 得到本體碼,因一代身份證皆為19XX年生人,年份中增加19,組成4位
    String masterNumber = IDNo15.substring(0, 6) + "19" + IDNo15.substring(6);
    // 計(jì)算校驗(yàn)碼
    String checkNumber = computeCheckNumber(masterNumber);
    // 返回本體碼+校驗(yàn)碼=完整的身份證號(hào)碼
    return masterNumber + checkNumber;
}

同余

同余的定義

給定一個(gè)正整數(shù)m,如果兩個(gè)整數(shù)a和b滿足a-b能夠被m整除,即(a-b)/m得到一個(gè)整數(shù),那么就稱整數(shù)a與b對(duì)模m同余,記作a \equiv b \pmod m

同余的性質(zhì)

  1. 反身性:a \equiv a \pmod m
  2. 對(duì)稱性:若a \equiv b \pmod m,則b \equiv a \pmod m
  3. 傳遞性:若a \equiv b \pmod mb \equiv c \pmod m,則a \equiv c \pmod m
  4. 同余式相加:若a \equiv b \pmod mb \equiv d \pmod m,則a \pm c \equiv b \pm d \pmod m
  5. 同余式相乘:若a \equiv b \pmod mc \equiv d \pmod m,則ac \equiv bd \pmod m

校驗(yàn)碼計(jì)算規(guī)則章節(jié)中,用到了以下公式
( a + b ) \pmod{m} = ( a \mod m + b \mod m ) \pmod{m}
我們以此為例進(jìn)行證明,設(shè)
\begin{align*} a = q_{1}m + r_{1} \\ b = q_{2}m + r_{2} \end{align*}

\begin{align*} ( a + b ) \pmod{m} &= ( q_{1}m + r_{1} + q_{2}m + r_{2} ) \pmod{m} \\ &= ( ( q_{1} + q_{2} ) m + r_{1} + r_{2} ) \pmod{m} \\ &= ( r_{1} + r_{2} ) \pmod{m} \\ &= ( a \mod m + b \mod m ) \pmod{m} \\ \end{align*}

校驗(yàn)字符系統(tǒng)

關(guān)于校驗(yàn)字符系統(tǒng),其國(guó)際標(biāo)準(zhǔn)ISO 7064有2個(gè)版本,分別是ISO 7064:1983和ISO/IEC 7064:2003,從內(nèi)容上來(lái)說(shuō),除了表面的調(diào)整,本質(zhì)上沒(méi)有區(qū)別,我想可以理解為是IEC成立后對(duì)其工作范圍主權(quán)的宣示。那么,對(duì)應(yīng)的國(guó)家標(biāo)準(zhǔn),也有了2個(gè)版本,分別是GB/T 17710-1999和GB/T 17710-2008,基本上保證了對(duì)國(guó)際標(biāo)準(zhǔn)的高水準(zhǔn)翻譯水平,使英文閱讀能力欠佳的讀者可以通過(guò)國(guó)家標(biāo)準(zhǔn)來(lái)體會(huì)國(guó)際標(biāo)準(zhǔn)制定的嚴(yán)謹(jǐn),并從中受益。

標(biāo)準(zhǔn)中,提供了如下幾個(gè)校驗(yàn)字符系統(tǒng),基本涵蓋日常所需。身份證號(hào)碼校驗(yàn)使用的ISO 7064, MOD 11-2,便是其中之一。在實(shí)際項(xiàng)目中,可按需選用。

系統(tǒng)類型 系統(tǒng)名稱 適用范圍 校驗(yàn)碼數(shù)目及類型 數(shù)字表示法
純系統(tǒng) ISO 7064, MOD 11-2 數(shù)字 1位數(shù)字或附加符X 1
純系統(tǒng) ISO 7064, MOD 37-2 字母數(shù)字 1位數(shù)字或字母或附加符* 2
純系統(tǒng) ISO 7064, MOD 97-10 數(shù)字 2位數(shù)字 3
純系統(tǒng) ISO 7064, MOD 661-26 字母 2位字母 4
純系統(tǒng) ISO 7064, MOD 1271-36 字母數(shù)字 2位數(shù)字或字母 5
混合系統(tǒng) ISO 7064, MOD 11,10 數(shù)字 1位數(shù)字 6
混合系統(tǒng) ISO 7064, MOD 27,26 字母 1位字母 7
混合系統(tǒng) ISO 7064, MOD 37,36 字母數(shù)字 1位數(shù)字或字母 8

表格中可見(jiàn),校驗(yàn)字符系統(tǒng),包括純系統(tǒng)和混合系統(tǒng)。使用一個(gè)模數(shù)的稱為純系統(tǒng),系統(tǒng)名稱中MOD后第1個(gè)數(shù)字是模數(shù),第2個(gè)數(shù)字是基數(shù);使用兩個(gè)模數(shù)的稱為混合系統(tǒng),系統(tǒng)名稱中MOD后的2個(gè)數(shù)字都是模數(shù)。

純系統(tǒng)

純系統(tǒng)又包括使用一個(gè)校驗(yàn)字符和使用兩個(gè)校驗(yàn)字符。使用一個(gè)校驗(yàn)字符的純系統(tǒng)與使用兩個(gè)校驗(yàn)字符的純系統(tǒng)本質(zhì)上是相同的,都遵守校驗(yàn)公式:
\sum_{i=1}^{n} ( a_{i} \times r^{i-1} ) \equiv 1 \pmod{M}

  • n - 包括校驗(yàn)字符在內(nèi)的字符串的字符個(gè)數(shù);
  • i - 表示從右至左包括校驗(yàn)碼字符在內(nèi)的字符的位置索引,即最右邊的字符i=1
  • a_{i} - 表示第i位置上字符值;
  • r - 基數(shù);
  • M - 模數(shù)。

只不過(guò),使用一個(gè)校驗(yàn)字符的純系統(tǒng)與使用兩個(gè)校驗(yàn)字符的純系統(tǒng)在計(jì)算校驗(yàn)字符的方式上略有不同:

  • 使用一個(gè)校驗(yàn)字符的純系統(tǒng)的校驗(yàn)字符計(jì)算公式
    \begin{align*} & ( \sum_{i=2}^{n} ( a_{i} \times r^{i-1} ) + a_{1} ) \equiv 1 \pmod{M} \\ \Rightarrow & a_{1} = ( M + 1 - \sum_{i=2}^{n} ( a_{i} \times r^{i-1} ) \pmod{M} ) \pmod{M} \end{align*}
    其中a_{1}是校驗(yàn)字符,r^{i-1}也可以使用W_{i}替代,W_{i} = r^{i-1} \pmod{M}

  • 使用兩個(gè)校驗(yàn)字符的純系統(tǒng)的校驗(yàn)字符計(jì)算公式
    \begin{align*} & ( \sum_{i=3}^{n} ( a_{i} \times r^{i-1} ) + a_{2} \times r + a_{1} ) \equiv 1 \pmod{M} \\ & a_{2} \times r + a_{1} = ( M + 1 - \sum_{i=3}^{n} ( a_{i} \times r^{i-1} ) \pmod{M} ) \pmod{M} \\ \Rightarrow & a_{2} = ( ( M + 1 - \sum_{i=3}^{n} ( a_{i} \times r^{i-1} ) \pmod{M} ) \pmod{M} ) / r \\ & a_{1} = ( ( M + 1 - \sum_{i=3}^{n} ( a_{i} \times r^{i-1} ) \pmod{M} ) \pmod{M} ) \pmod{r} \end{align*}
    其中a_{2}a_{1}是校驗(yàn)字符,r^{i-1}也可以使用W_{i}替代,W_{i} = r^{i-1} \pmod{M}

純系統(tǒng)有兩種基本的計(jì)算方法,純系統(tǒng)遞歸法和純系統(tǒng)多項(xiàng)式法:

  • 遞歸法
    從左往右計(jì)算:
    ( \cdots ((a_{n} \times r + a_{n-1}) \times r + a_{n-2}) \times r + \cdots + a_{1}) \equiv 1 \pmod M

  • 多項(xiàng)式法
    使用\sum公式表示:
    \sum_{i=1}^{n}(a_{i} \times r^{i-1} \pmod{M}) \equiv 1 \pmod M

其實(shí)遞歸法的計(jì)算完全展開(kāi),得到的也就是多項(xiàng)式法,所以兩種計(jì)算方法產(chǎn)生相同的結(jié)果。

混合系統(tǒng)

混合系統(tǒng)使用一個(gè)校驗(yàn)字符,遵守校驗(yàn)公式:
( \cdots ((((((M+a_{n})||_{M}\times 2)|_{(M+1)}+a_{n-1})||_{M}\times 2)|_{(M+1)}+ \cdots + a_{i})||_{M}\times 2)|_{(M+1)} \cdots +a_{1})||_{M}=1

  • n - 包括校驗(yàn)字符在內(nèi)的字符串的字符個(gè)數(shù);
  • i - 表示從右至左包括校驗(yàn)碼字符在內(nèi)的字符的位置索引,即最右邊的字符i=1
  • a_{i} - 表示第i位置上字符值;
  • M(M+1) - 兩個(gè)模數(shù);
  • ||_{M} - 除以M后的余數(shù),如果其值為0,則用M代替;
  • |_{(M+1)} - 除以(M+1)后的余數(shù),在經(jīng)過(guò)上述處理后,余數(shù)絕不會(huì)為0。

上述公式本身就是遞歸法,無(wú)法展開(kāi),因此混合系統(tǒng)僅支持遞歸法計(jì)算,不支持多項(xiàng)式法。

最后,附上大家喜聞樂(lè)見(jiàn)的代碼。

package com.godson.util;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * ISO7064工具類
 * <p>
 * 使用ISO7064規(guī)范中定義的校驗(yàn)字符系統(tǒng)進(jìn)行字符串的校驗(yàn)以及生成校驗(yàn)字符
 * </p>
 * 
 */
public class ISO7064Util {

    /**
     * ISO7064規(guī)范中定義的校驗(yàn)字符系統(tǒng)
     * <p>
     * <li>ISO 7064, MOD 11-2使用 {@link #ISO_7064_MOD_11_2}表示
     * </li>
     * <li>ISO 7064, MOD 37-2使用{@link #ISO_7064_MOD_37_2}表示</li>
     * <li>ISO 7064, MOD 97-10使用{@link #ISO_7064_MOD_97_10}
     * 表示</li>
     * <li>
     * ISO 7064, MOD 661-26使用 {@link #ISO_7064_MOD_661_26}表示
     * </li>
     * <li>ISO 7064, MOD 1271-36使用
     * {@link #ISO_7064_MOD_1271_36}表示</li>
     * <li>ISO 7064, MOD 11,10使用
     * {@link #ISO_7064_MOD_11_HYBRID_10}表示</li>
     * <li>ISO 7064, MOD 27,26使用
     * {@link #ISO_7064_MOD_27_HYBRID_26}表示</li>
     * <li>ISO 7064, MOD 37,36使用
     * {@link #ISO_7064_MOD_37_HYBRID_36}表示</li>
     */
    public enum Designation {
        /** ISO 7064, MOD 11-2 */
        ISO_7064_MOD_11_2,
        /** ISO 7064, MOD 37-2 */
        ISO_7064_MOD_37_2,
        /** ISO 7064, MOD 97-10 */
        ISO_7064_MOD_97_10,
        /** ISO 7064, MOD 661-26 */
        ISO_7064_MOD_661_26,
        /** ISO 7064, MOD 1271-36 */
        ISO_7064_MOD_1271_36,
        /** ISO 7064, MOD 11,10 */
        ISO_7064_MOD_11_HYBRID_10,
        /** ISO 7064, MOD 27,26 */
        ISO_7064_MOD_27_HYBRID_26,
        /** ISO 7064, MOD 37,36 */
        ISO_7064_MOD_37_HYBRID_36
    }

    /**
     * 計(jì)算校驗(yàn)字符
     * 
     * @param withoutCheckCharacterString 不含校驗(yàn)字符的字符串
     * @param designation 校驗(yàn)字符系統(tǒng)
     * @return 校驗(yàn)字符
     * @throws IllegalArgumentException
     *             如果字符串不匹配對(duì)應(yīng)校驗(yàn)字符系統(tǒng)的正則表達(dá)式
     */
    public static String computeCheckCharacter(
            String withoutCheckCharacterString, Designation designation) {
        // 檢查字符串是否匹配對(duì)應(yīng)校驗(yàn)字符系統(tǒng)的正則表達(dá)式
        if (!RegexMatcher.withoutCheckCharacterStringIsMatch(
                withoutCheckCharacterString, designation)) {
            throw new IllegalArgumentException();
        }
        // 計(jì)算校驗(yàn)字符
        return CheckCharacterComputor.compute(withoutCheckCharacterString,
                designation);
    }

    /**
     * 校驗(yàn)字符串
     * 
     * @param withCheckCharacterString 含校驗(yàn)字符的字符串
     * @param designation 校驗(yàn)字符系統(tǒng)
     * @return true - 校驗(yàn)通過(guò)<br>
     *         false-校驗(yàn)不通過(guò)
     * @throws IllegalArgumentException
     *             如果字符串不匹配對(duì)應(yīng)校驗(yàn)字符系統(tǒng)的正則表達(dá)式
     */
    public static boolean checkString(String withCheckCharacterString,
            Designation designation) {
        // 檢查字符串是否匹配對(duì)應(yīng)校驗(yàn)字符系統(tǒng)的正則表達(dá)式
        if (!RegexMatcher.withCheckCharacterStringIsMatch(
                withCheckCharacterString, designation)) {
            throw new IllegalArgumentException();
        }
        // 校驗(yàn)字符串
        return CheckCharacterSystemValidator.validate(withCheckCharacterString,
                designation);
    }

    /**
     * 正則表達(dá)式匹配器
     * <p>
     * 檢查字符串是否匹配對(duì)應(yīng)校驗(yàn)字符系統(tǒng)的正則表達(dá)式
     * </p>
     * <table border="1">
     * <tr>
     * <th>系統(tǒng)名稱</th>
     * <th>適用范圍</th>
     * <th>校驗(yàn)碼數(shù)目及類型</th>
     * </tr>
     * <tr>
     * <td>ISO 7064, MOD 11-2</td>
     * <td>數(shù)字</td>
     * <td>1位數(shù)字或附加符X</td>
     * </tr>
     * <tr>
     * <td>ISO 7064, MOD 37-2</td>
     * <td>字母數(shù)字</td>
     * <td>1位數(shù)字或字母或附加符*</td>
     * </tr>
     * <tr>
     * <td>ISO 7064, MOD 97-10</td>
     * <td>數(shù)字</td>
     * <td>2位數(shù)字</td>
     * </tr>
     * <tr>
     * <td>ISO 7064, MOD 661-26</td>
     * <td>字母</td>
     * <td>2位字母</td>
     * </tr>
     * <tr>
     * <td>ISO 7064, MOD 1271-36</td>
     * <td>字母數(shù)字</td>
     * <td>2位數(shù)字或字母</td>
     * </tr>
     * <tr>
     * <td>ISO 7064, MOD 11,10</td>
     * <td>數(shù)字</td>
     * <td>1位數(shù)字</td>
     * </tr>
     * <tr>
     * <td>ISO 7064, MOD 27,26</td>
     * <td>字母</td>
     * <td>1位字母</td>
     * </tr>
     * <tr>
     * <td>ISO 7064, MOD 37,36</td>
     * <td>字母數(shù)字</td>
     * <td>1位數(shù)字或字母</td>
     * </tr>
     * </table>
     */
    private static class RegexMatcher {

        /**
         * 檢查不含校驗(yàn)字符的字符串是否匹配對(duì)應(yīng)校驗(yàn)字符系統(tǒng)的正則表達(dá)式
         * 
         * @param withoutCheckCharacterString 不含校驗(yàn)字符的字符串
         * @param designation 校驗(yàn)字符系統(tǒng)
         * @return true - 匹配<br>
         *         false - 不匹配
         */
        static boolean withoutCheckCharacterStringIsMatch(
                String withoutCheckCharacterString, Designation designation) {
            return regexMatch(withoutCheckCharacterString,
                    REGEX_MAPPING_WITHOUT_CHECK_CHARACTER_STRING
                            .get(designation));
        }

        /**
         * 檢查有校驗(yàn)字符的字符串是否匹配對(duì)應(yīng)校驗(yàn)字符系統(tǒng)的正則表達(dá)式
         * 
         * @param withCheckCharacterString 含校驗(yàn)字符的字符串
         * @param designation 校驗(yàn)字符系統(tǒng)
         * @return true - 匹配<br>
         *         false - 不匹配
         */
        static boolean withCheckCharacterStringIsMatch(
                String withCheckCharacterString, Designation designation) {
            return regexMatch(withCheckCharacterString,
                    REGEX_MAPPING_WITH_CHECK_CHARACTER_STRING.get(designation));
        }

        /** 數(shù)字正則表達(dá)式 */
        static final String REGEX_NUMBERIC_STRINGS = "^[0-9]+$";
        /** 含補(bǔ)充校驗(yàn)字符X的數(shù)字正則表達(dá)式 */
        static final String REGEX_NUMBERIC_STRINGS_WITH_SUPPLEMENTARY_CHECK_CHARACTER = "^[0-9]+[0-9X]$";
        /** 字母正則表達(dá)式 */
        static final String REGEX_ALPHABETIC_STRINGS = "^[A-Z]+$";
        /** 字母數(shù)字正則表達(dá)式 */
        static final String REGEX_ALPHANUMBERIC_STRINGS = "^[0-9A-Z]+$";
        /** 含補(bǔ)充校驗(yàn)字符*的字母數(shù)字表達(dá)式 */
        static final String REGEX_ALPHANUMBERIC_STRINGS_WITH_SUPPLEMENTARY_CHECK_CHARACTER = "^[0-9A-Z]+[0-9A-Z*]$";

        /** 校驗(yàn)字符系統(tǒng)對(duì)應(yīng)的正則表達(dá)式(不含校驗(yàn)字符) */
        @SuppressWarnings("serial")
        static final Map<Designation, String> REGEX_MAPPING_WITHOUT_CHECK_CHARACTER_STRING = new HashMap<Designation, String>() {
            {
                put(Designation.ISO_7064_MOD_11_2, REGEX_NUMBERIC_STRINGS);
                put(Designation.ISO_7064_MOD_37_2, REGEX_ALPHANUMBERIC_STRINGS);
                put(Designation.ISO_7064_MOD_97_10, REGEX_NUMBERIC_STRINGS);
                put(Designation.ISO_7064_MOD_661_26, REGEX_ALPHABETIC_STRINGS);
                put(Designation.ISO_7064_MOD_1271_36,
                        REGEX_ALPHANUMBERIC_STRINGS);
                put(Designation.ISO_7064_MOD_11_HYBRID_10,
                        REGEX_NUMBERIC_STRINGS);
                put(Designation.ISO_7064_MOD_27_HYBRID_26,
                        REGEX_ALPHABETIC_STRINGS);
                put(Designation.ISO_7064_MOD_37_HYBRID_36,
                        REGEX_ALPHANUMBERIC_STRINGS);
            }
        };

        /** 校驗(yàn)字符系統(tǒng)對(duì)應(yīng)的正則表達(dá)式(含校驗(yàn)字符) */
        @SuppressWarnings("serial")
        static final Map<Designation, String> REGEX_MAPPING_WITH_CHECK_CHARACTER_STRING = new HashMap<Designation, String>() {
            {
                put(Designation.ISO_7064_MOD_11_2,
                        REGEX_NUMBERIC_STRINGS_WITH_SUPPLEMENTARY_CHECK_CHARACTER);
                put(Designation.ISO_7064_MOD_37_2,
                        REGEX_ALPHANUMBERIC_STRINGS_WITH_SUPPLEMENTARY_CHECK_CHARACTER);
                put(Designation.ISO_7064_MOD_97_10, REGEX_NUMBERIC_STRINGS);
                put(Designation.ISO_7064_MOD_661_26, REGEX_ALPHABETIC_STRINGS);
                put(Designation.ISO_7064_MOD_1271_36,
                        REGEX_ALPHANUMBERIC_STRINGS);
                put(Designation.ISO_7064_MOD_11_HYBRID_10,
                        REGEX_NUMBERIC_STRINGS);
                put(Designation.ISO_7064_MOD_27_HYBRID_26,
                        REGEX_ALPHABETIC_STRINGS);
                put(Designation.ISO_7064_MOD_37_HYBRID_36,
                        REGEX_ALPHANUMBERIC_STRINGS);
            }
        };

        static boolean regexMatch(String inputString, String regex) {
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(inputString);
            return matcher.matches();
        }
    }

    /** 適用于數(shù)字的校驗(yàn)字符系統(tǒng)的數(shù)值對(duì)應(yīng)表 */
    private static final String[] NUMBERIC_STRINGS = { "0", "1", "2", "3", "4",
            "5", "6", "7", "8", "9", "X" };
    /** 適用于字母的校驗(yàn)字符系統(tǒng)的數(shù)值對(duì)應(yīng)表 */
    private static final String[] ALPHABETIC_STRINGS = { "A", "B", "C", "D",
            "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
            "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
    /** 適用于字母數(shù)字的校驗(yàn)字符系統(tǒng)的數(shù)值對(duì)應(yīng)表 */
    private static final String[] ALPHANUMBERIC_STRINGS = { "0", "1", "2", "3",
            "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G",
            "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
            "U", "V", "W", "X", "Y", "Z", "*" };

    /**
     * 校驗(yàn)字符系統(tǒng)驗(yàn)證器
     */
    private static class CheckCharacterSystemValidator {
        static boolean validate(String inputString, Designation designation) {
            switch (designation) {
                case ISO_7064_MOD_11_2:
                case ISO_7064_MOD_37_2:
                case ISO_7064_MOD_97_10:
                case ISO_7064_MOD_661_26:
                case ISO_7064_MOD_1271_36:
                    return validatePureSystem(inputString, designation);
                case ISO_7064_MOD_11_HYBRID_10:
                case ISO_7064_MOD_27_HYBRID_26:
                case ISO_7064_MOD_37_HYBRID_36:
                    return validateHybridSystem(inputString, designation);
                default:
                    return false;
            }
        }

        /**
         * 純系統(tǒng)校驗(yàn)
         */
        static boolean validatePureSystem(String inputString,
                Designation designation) {
            int M = 0; // 模數(shù)
            int r = 0; // 基數(shù)
            List<String> mapping = null;
            switch (designation) {
                case ISO_7064_MOD_11_2:
                    M = 11;
                    r = 2;
                    mapping = Arrays.asList(NUMBERIC_STRINGS);
                    break;
                case ISO_7064_MOD_37_2:
                    M = 37;
                    r = 2;
                    mapping = Arrays.asList(ALPHANUMBERIC_STRINGS);
                    break;
                case ISO_7064_MOD_97_10:
                    M = 97;
                    r = 10;
                    mapping = Arrays.asList(NUMBERIC_STRINGS);
                    break;
                case ISO_7064_MOD_661_26:
                    M = 661;
                    r = 26;
                    mapping = Arrays.asList(ALPHABETIC_STRINGS);
                    break;
                case ISO_7064_MOD_1271_36:
                    M = 1271;
                    r = 36;
                    mapping = Arrays.asList(ALPHANUMBERIC_STRINGS);
                    break;
                default:
                    return false;
            }
            char[] strArray = inputString.toCharArray();
            int S = 0;
            int n = strArray.length;
            for (int i = 1; i <= n; i++) {
                // 注意這里不要使用Math的pow方法
                S += mapping.indexOf(String.valueOf(strArray[i - 1]))
                        * BigInteger.valueOf(r).pow(n - i)
                                .mod(BigInteger.valueOf(M)).intValue();
            }
            return S % M == 1;
        }

        /**
         * 混合系統(tǒng)校驗(yàn)
         */
        static boolean validateHybridSystem(String inputString,
                Designation designation) {
            int M = 0; // 模數(shù)1
            List<String> mapping = null;

            switch (designation) {
                case ISO_7064_MOD_11_HYBRID_10:
                    M = 10;
                    mapping = Arrays.asList(NUMBERIC_STRINGS);
                    break;
                case ISO_7064_MOD_27_HYBRID_26:
                    M = 26;
                    mapping = Arrays.asList(ALPHABETIC_STRINGS);
                    break;
                case ISO_7064_MOD_37_HYBRID_36:
                    M = 36;
                    mapping = Arrays.asList(ALPHANUMBERIC_STRINGS);
                    break;
                default:
                    return false;
            }
            int Mplus1 = M + 1; // 模數(shù)2
            char[] strArray = inputString.toCharArray();
            int S = M + mapping.indexOf(String.valueOf(strArray[0]));
            int P = 0;
            for (int i = 1; i < strArray.length; i++) {
                P = ((S % M == 0 ? M : S % M) * 2) % Mplus1;
                S = P + mapping.indexOf(String.valueOf(strArray[i]));
            }
            return S % M == 1;
        }
    }

    /**
     * 校驗(yàn)字符生成器
     */
    private static class CheckCharacterComputor {
        static String compute(String inputString, Designation designation) {
            switch (designation) {
                case ISO_7064_MOD_11_2:
                case ISO_7064_MOD_37_2:
                    return polynomialMethod4PureSystemWith1CheckChar(
                            inputString, designation);
                case ISO_7064_MOD_97_10:
                case ISO_7064_MOD_661_26:
                case ISO_7064_MOD_1271_36:
                    return polynomialMethod4PureSystemWith2CheckChar(
                            inputString, designation);
                case ISO_7064_MOD_11_HYBRID_10:
                case ISO_7064_MOD_27_HYBRID_26:
                case ISO_7064_MOD_37_HYBRID_36:
                    return recursiveMethod4HybridSystemWith1CheckChar(
                            inputString, designation);
                default:
                    return null;
            }
        }

        /**
         * 通過(guò)多項(xiàng)式法計(jì)算純系統(tǒng)一位校驗(yàn)字符
         */
        static String polynomialMethod4PureSystemWith1CheckChar(String str,
                Designation designation) {
            int M = 0; // 模數(shù)
            int r = 0; // 基數(shù)
            List<String> mapping = null;
            switch (designation) {
                case ISO_7064_MOD_11_2:
                    M = 11;
                    r = 2;
                    mapping = Arrays.asList(NUMBERIC_STRINGS);
                    break;
                case ISO_7064_MOD_37_2:
                    M = 37;
                    r = 2;
                    mapping = Arrays.asList(ALPHANUMBERIC_STRINGS);
                    break;
                default:
                    break;
            }
            char[] strArray = str.toCharArray();
            int S = 0;
            int n = strArray.length + 1;
            for (int i = n; i >= 2; i--) {
                // 注意這里不要使用Math的pow方法
                S += mapping.indexOf(String.valueOf(strArray[n - i]))
                        * BigInteger.valueOf(r).pow(i - 1)
                                .mod(BigInteger.valueOf(M)).intValue();
            }
            return mapping.get((M + 1 - S % M) % M);
        }

        /**
         * 通過(guò)多項(xiàng)式法計(jì)算純系統(tǒng)二位校驗(yàn)字符
         */
        static String polynomialMethod4PureSystemWith2CheckChar(String str,
                Designation designation) {
            int M = 0; // 模數(shù)
            int r = 0; // 基數(shù)
            List<String> mapping = null;
            switch (designation) {
                case ISO_7064_MOD_97_10:
                    M = 97;
                    r = 10;
                    mapping = Arrays.asList(NUMBERIC_STRINGS);
                    break;
                case ISO_7064_MOD_661_26:
                    M = 661;
                    r = 26;
                    mapping = Arrays.asList(ALPHABETIC_STRINGS);
                    break;
                case ISO_7064_MOD_1271_36:
                    M = 1271;
                    r = 36;
                    mapping = Arrays.asList(ALPHANUMBERIC_STRINGS);
                    break;
                default:
                    break;
            }
            char[] strArray = str.toCharArray();
            int S = 0;
            int n = strArray.length + 2;
            for (int i = n; i >= 3; i--) {
                // 注意這里不要使用Math的pow方法
                S += mapping.indexOf(String.valueOf(strArray[n - i]))
                        * BigInteger.valueOf(r).pow(i - 1)
                                .mod(BigInteger.valueOf(M)).intValue();
            }
            return mapping.get(((M + 1 - S % M) % M) / r)
                    + mapping.get(((M + 1 - S % M) % M) % r);
        }

        /**
         * 通過(guò)遞歸法法計(jì)算混合系統(tǒng)一位校驗(yàn)字符
         */
        static String recursiveMethod4HybridSystemWith1CheckChar(
                String inputString, Designation designation) {
            int M = 0; // 模數(shù)1
            List<String> mapping = null;
            switch (designation) {
                case ISO_7064_MOD_11_HYBRID_10:
                    M = 10;
                    mapping = Arrays.asList(NUMBERIC_STRINGS);
                    break;
                case ISO_7064_MOD_27_HYBRID_26:
                    M = 26;
                    mapping = Arrays.asList(ALPHABETIC_STRINGS);
                    break;
                case ISO_7064_MOD_37_HYBRID_36:
                    M = 36;
                    mapping = Arrays.asList(ALPHANUMBERIC_STRINGS);
                    break;
                default:
                    break;
            }
            int Mplus1 = M + 1; // 模數(shù)2
            char[] strArray = inputString.toCharArray();
            int S = 0;
            int P = M;
            int n = strArray.length + 1;
            for (int i = n; i >= 2; i--) {
                S = P + mapping.indexOf(String.valueOf(strArray[n - i]));
                P = ((S % M == 0 ? M : S % M) * 2) % Mplus1;
            }
            return mapping.get((M + 1 - P % M) % M);
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 申明:本文除特別說(shuō)明外,身份證號(hào)碼專指18位公民身份號(hào)碼 一、身份證號(hào)碼結(jié)構(gòu) 早期‘身份證號(hào)碼’叫‘社會(huì)保障號(hào)’,...
    安東的漫長(zhǎng)歲月閱讀 21,814評(píng)論 0 4
  • 簡(jiǎn)言 在做用戶實(shí)名驗(yàn)證時(shí),常會(huì)用到身份證號(hào)碼的正則表達(dá)式及校驗(yàn)方案。本文列舉了兩種驗(yàn)證方案,大家可以根據(jù)自己的項(xiàng)目...
    毛三十閱讀 1,264評(píng)論 0 5
  • 在進(jìn)行互聯(lián)網(wǎng)產(chǎn)品設(shè)計(jì)時(shí),尤其是互聯(lián)網(wǎng)金融產(chǎn)品,實(shí)名認(rèn)證是很重要的環(huán)節(jié)。今天和大家聊聊一個(gè)很容易被忽視的話題:身份證...
    威理閱讀 1,495評(píng)論 4 7
  • 時(shí)間最殘忍。往昔尚如昨,轉(zhuǎn)眼間我們已經(jīng)畢業(yè)分別十年。十年前,我們不懂離別的哀愁,不懂前途的兇險(xiǎn),錯(cuò)以為,時(shí)光永遠(yuǎn)不...
    黃淮雄風(fēng)閱讀 407評(píng)論 2 2
  • 文 / 瀟 萱 晴朗湛藍(lán)的天空 情人節(jié) 涌出無(wú)數(shù)的溫情 醇香的芒果千層 甜蜜著節(jié)日的氣氛 迷人的藍(lán)色妖姬 成為第二...
    瀟萱閱讀 241評(píng)論 0 1