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

前言

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

筆者目前使用的jdk版本是1.6.0_29筑悴,Eclipse版本是Juno Release们拙,Build id 20120614-1722。如無特殊說明阁吝,本文所有的Java代碼都是基于此睛竣。

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

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

修訂記錄

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

參考資料

  1. 中華人民共和國居民身份證法
  2. 中華人民共和國國家標(biāo)準(zhǔn) GB/T 2260-2007 中華人民共和國行政區(qū)劃代碼
  3. 中華人民共和國國家標(biāo)準(zhǔn) GB 11643-1999 公民身份證號碼
  4. 中華人民共和國國家標(biāo)準(zhǔn) GB/T 17710-1999 數(shù)據(jù)處理 校驗(yàn)碼系統(tǒng)
  5. 中華人民共和國國家標(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

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

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

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

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

  1. 一代身份證是15位装蓬,二代身份證是18位著拭;
  2. 一代身份證出生年月日采用YYMMDD格式,二代身份證出生年月日采用YYYYMMDD格式牍帚;
  3. 一代身份證無校驗(yàn)碼儡遮,二代身份證有校驗(yàn)碼。

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

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

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

舉例說明:

筆者身份證號碼為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)證自己的身份證號碼。

了解了身份證號碼的校驗(yàn)公式后绩衷,根據(jù)同余定理可證得身份證號碼校驗(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ù)字符X表示。此處需要注意:是羅馬數(shù)字X咳燕,不應(yīng)理解為英文字母X勿决。

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

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

  1. 身份證號碼前6位,可采集客戶戶籍所在地宪肖,只需將國家標(biāo)準(zhǔn)GB/T 2260中定義的行政區(qū)劃代碼導(dǎo)入數(shù)據(jù)庫表制,程序中進(jìn)行映射即可;但需要注意的是控乾,行政區(qū)劃代碼每隔幾年會修訂一次么介,從筆者手上的2007版來看,共經(jīng)歷了1982年蜕衡、1984年壤短、1986年、1988年慨仿、1991年久脯、1995年、1999年镰吆、2002年帘撰、2007年九次修訂,所以要預(yù)留更新機(jī)制万皿;
  2. 身份證號碼7到14位摧找,可采集客戶的出生日期、年齡牢硅、生日蹬耘;
  3. 身份證號碼17位,可采集客戶的性別减余,奇數(shù)表示男性综苔,偶數(shù)表示女性;
  4. 身份證號碼的驗(yàn)證渠道,不管驗(yàn)證成功與否如筛,往往都是收費(fèi)的堡牡,比如銀行渠道、公安部渠道妙黍,在發(fā)往這些渠道驗(yàn)證之前悴侵,先對其進(jìn)行長度、正則表達(dá)式拭嫁、校驗(yàn)碼的驗(yàn)證,能夠適當(dāng)提高收費(fèi)驗(yàn)證的成功率抓于,節(jié)省成本支出做粤;而且也可以提升用戶體驗(yàn),在用戶輸入錯誤時及時反饋而不必等待驗(yàn)證渠道結(jié)果的返回捉撮。

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

/**
 * 18位二代身份證號碼的正則表達(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位一代身份證號碼的正則表達(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)身份證號碼:

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

/**
 * 校驗(yàn)字符串長度
 * 
 * @param inputString 字符串
 * @param len 預(yù)期長度
 * @return true - 校驗(yàn)通過<br>
 *         false - 校驗(yàn)不通過
 */
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)通過<br>
 *         false - 校驗(yàn)不通過
 */
private static boolean regexMatch(String inputString, String regex) {
    return inputString.matches(regex);
}

/**
 * 校驗(yàn)碼校驗(yàn)
 * <p>
 * 適用于18位的二代身份證號碼
 * </p>
 * 
 * @param IDNo18 身份證號碼
 * @return true - 校驗(yàn)通過<br>
 *         false - 校驗(yàn)不通過
 */
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)通過
    return sum % 11 == 1;
}

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

/**
 * 計(jì)算身份證號碼的校驗(yàn)碼
 * <p>
 * 適用于18位的二代身份證號碼吼和,身份證號碼由17位本體碼和1位校驗(yàn)碼組成
 * </p>
 * 
 * @param masterNumber 本體碼
 * @return 身份證號碼
 * @throws IllegalArgumentException 
 *             如果本體碼為空或長度不為17位或不滿足本體碼組成規(guī)則
 *             <i>6位地址碼+
 *             出生年月日YYYYMMDD+3位順序碼</i>
 */
public static String computeIDNoCheckNumber(String masterNumber) {
    // 校驗(yàn)本體碼的長度
    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)碼=完整的身份證號碼
    return masterNumber + checkNumber;
}

/**
 * 計(jì)算校驗(yàn)碼
 * <p>
 * 適用于18位的二代身份證號碼
 * </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位的一代身份證升級為18位的二代身份證的情形骑素,代碼示例如下:

/**
 * 15位一代身份證號碼升級18位二代身份證號碼
 * <p>
 * 為15位的一代身份證號碼增加年份的前2位和最后1位校驗(yàn)碼
 * </p>
 * 
 * @param IDNo15 15位的一代身份證號碼
 * @return 18位的二代身份證號碼
 */
public static String updateIDNo15to18(String IDNo15) {
    // 校驗(yàn)身份證號碼的長度
    if (!checkStrLength(IDNo15, 15)) {
        throw new IllegalArgumentException();
    }
    // 匹配身份證號碼的正則表達(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)碼=完整的身份證號碼
    return masterNumber + checkNumber;
}

同余

同余的定義

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

同余的性質(zhì)

  1. 反身性:a \equiv a \pmod m妥畏;
  2. 對稱性:若a \equiv b \pmod m邦邦,則b \equiv a \pmod m
  3. 傳遞性:若a \equiv b \pmod m咖熟,b \equiv c \pmod m圃酵,則a \equiv c \pmod m
  4. 同余式相加:若a \equiv b \pmod m馍管,b \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)拉盾,其國際標(biāo)準(zhǔn)ISO 7064有2個版本,分別是ISO 7064:1983和ISO/IEC 7064:2003豁状,從內(nèi)容上來說捉偏,除了表面的調(diào)整,本質(zhì)上沒有區(qū)別泻红,我想可以理解為是IEC成立后對其工作范圍主權(quán)的宣示夭禽。那么,對應(yīng)的國家標(biāo)準(zhǔn)谊路,也有了2個版本讹躯,分別是GB/T 17710-1999和GB/T 17710-2008,基本上保證了對國際標(biāo)準(zhǔn)的高水準(zhǔn)翻譯水平缠劝,使英文閱讀能力欠佳的讀者可以通過國家標(biāo)準(zhǔn)來體會國際標(biāo)準(zhǔn)制定的嚴(yán)謹(jǐn)潮梯,并從中受益。

標(biāo)準(zhǔn)中惨恭,提供了如下幾個校驗(yàn)字符系統(tǒng)秉馏,基本涵蓋日常所需。身份證號碼校驗(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

表格中可見糊肤,校驗(yàn)字符系統(tǒng),包括純系統(tǒng)和混合系統(tǒng)氓鄙。使用一個模數(shù)的稱為純系統(tǒng)馆揉,系統(tǒng)名稱中MOD后第1個數(shù)字是模數(shù),第2個數(shù)字是基數(shù)抖拦;使用兩個模數(shù)的稱為混合系統(tǒng)升酣,系統(tǒng)名稱中MOD后的2個數(shù)字都是模數(shù)。

純系統(tǒng)

純系統(tǒng)又包括使用一個校驗(yàn)字符和使用兩個校驗(yàn)字符态罪。使用一個校驗(yàn)字符的純系統(tǒng)與使用兩個校驗(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)的字符串的字符個數(shù)瞒渠;
  • i - 表示從右至左包括校驗(yàn)碼字符在內(nèi)的字符的位置索引徽龟,即最右邊的字符i=1
  • a_{i} - 表示第i位置上字符值钦扭;
  • r - 基數(shù);
  • M - 模數(shù)凿菩。

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

  • 使用一個校驗(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}椒拗。

  • 使用兩個校驗(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ì)算完全展開玷氏,得到的也就是多項(xiàng)式法枉阵,所以兩種計(jì)算方法產(chǎn)生相同的結(jié)果。

混合系統(tǒng)

混合系統(tǒng)使用一個校驗(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)的字符串的字符個數(shù);
  • i - 表示從右至左包括校驗(yàn)碼字符在內(nèi)的字符的位置索引侦厚,即最右邊的字符i=1耻陕;
  • a_{i} - 表示第i位置上字符值;
  • M(M+1) - 兩個模數(shù)刨沦;
  • ||_{M} - 除以M后的余數(shù)诗宣,如果其值為0,則用M代替想诅;
  • |_{(M+1)} - 除以(M+1)后的余數(shù)召庞,在經(jīng)過上述處理后,余數(shù)絕不會為0来破。

上述公式本身就是遞歸法篮灼,無法展開,因此混合系統(tǒng)僅支持遞歸法計(jì)算徘禁,不支持多項(xiàng)式法诅诱。

最后,附上大家喜聞樂見的代碼送朱。

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

    /**
     * 正則表達(dá)式匹配器
     * <p>
     * 檢查字符串是否匹配對應(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)字符的字符串是否匹配對應(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)字符的字符串是否匹配對應(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)對應(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)對應(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ù)值對應(yīng)表 */
    private static final String[] NUMBERIC_STRINGS = { "0", "1", "2", "3", "4",
            "5", "6", "7", "8", "9", "X" };
    /** 適用于字母的校驗(yàn)字符系統(tǒng)的數(shù)值對應(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ù)值對應(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;
            }
        }

        /**
         * 通過多項(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);
        }

        /**
         * 通過多項(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);
        }

        /**
         * 通過遞歸法法計(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)容合作請聯(lián)系作者
  • 序言:七十年代末娘荡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子驶沼,更是在濱河造成了極大的恐慌炮沐,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件回怜,死亡現(xiàn)場離奇詭異大年,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門鲜戒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來专控,“玉大人,你說我怎么就攤上這事遏餐÷赘” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵失都,是天一觀的道長柏蘑。 經(jīng)常有香客問我,道長粹庞,這世上最難降的妖魔是什么咳焚? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮庞溜,結(jié)果婚禮上革半,老公的妹妹穿的比我還像新娘。我一直安慰自己流码,他們只是感情好又官,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著漫试,像睡著了一般六敬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上驾荣,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天外构,我揣著相機(jī)與錄音,去河邊找鬼播掷。 笑死审编,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的叮趴。 我是一名探鬼主播割笙,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼眯亦!你這毒婦竟也來了伤溉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤妻率,失蹤者是張志新(化名)和其女友劉穎乱顾,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宫静,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡走净,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年券时,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伏伯。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡橘洞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出说搅,到底是詐尸還是另有隱情炸枣,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布弄唧,位于F島的核電站适肠,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏候引。R本人自食惡果不足惜侯养,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望澄干。 院中可真熱鬧逛揩,春花似錦、人聲如沸麸俘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疾掰。三九已至,卻和暖如春徐紧,著一層夾襖步出監(jiān)牢的瞬間静檬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工并级, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拂檩,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓嘲碧,卻偏偏與公主長得像稻励,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子愈涩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

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

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