前言
根據(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ā)布 |
參考資料
- 中華人民共和國(guó)居民身份證法
- 中華人民共和國(guó)國(guó)家標(biāo)準(zhǔn) GB/T 2260-2007 中華人民共和國(guó)行政區(qū)劃代碼
- 中華人民共和國(guó)國(guó)家標(biāo)準(zhǔn) GB 11643-1999 公民身份證號(hào)碼
- 中華人民共和國(guó)國(guó)家標(biāo)準(zhǔn) GB/T 17710-1999 數(shù)據(jù)處理 校驗(yàn)碼系統(tǒng)
- 中華人民共和國(guó)國(guó)家標(biāo)準(zhǔn) GB/T 17710-2008 信息技術(shù) 安全技術(shù) 校驗(yàn)字符系統(tǒng)
- ISO 7064:1983 Data processing - Check character systems
- ISO/IEC 7064:2003 Information technology - Security techniques - Check character systems
身份證號(hào)碼的編碼規(guī)則
身份證號(hào)碼共18位,由17位本體碼和1位校驗(yàn)碼組成。
- 前6位是地址碼,表示登記戶口時(shí)所在地的行政區(qū)劃代碼,依照《中華人民共和國(guó)行政區(qū)劃代碼》國(guó)家標(biāo)準(zhǔn)(GB/T2260)的規(guī)定執(zhí)行;
- 7到14位是出生年月日,采用YYYYMMDD格式;
- 15到17位是順序碼,表示在同一地址碼所標(biāo)識(shí)的區(qū)域范圍內(nèi),對(duì)同年、同月、同日出生的人編訂的順序號(hào),順序碼的奇數(shù)分配給男性,偶數(shù)分配給女性,即第17位奇數(shù)表示男性,偶數(shù)表示女性;
- 第18位是校驗(yàn)碼,采用ISO 7064:1983, MOD 11-2校驗(yàn)字符系統(tǒng),計(jì)算規(guī)則下一章節(jié)說(shuō)明。
一代身份證與二代身份證的區(qū)別在于:
- 一代身份證是15位,二代身份證是18位;
- 一代身份證出生年月日采用YYMMDD格式,二代身份證出生年月日采用YYYYMMDD格式;
- 一代身份證無(wú)校驗(yàn)碼,二代身份證有校驗(yàn)碼。
校驗(yàn)碼計(jì)算規(guī)則
身份證號(hào)碼中各個(gè)位置上的號(hào)碼字符值應(yīng)滿足下列公式的校驗(yàn):
-
表示號(hào)碼字符
從右至左
包括校驗(yàn)碼字符在內(nèi)的位置序號(hào); -
表示第i位置上的號(hào)碼字符值,
是身份證號(hào)碼第18位校驗(yàn)碼;
-
表示第i位置上的加權(quán)因子,加權(quán)因子計(jì)算公式:
舉例說(shuō)明:
筆者身份證號(hào)碼為370683198901117657,根據(jù)上述公式進(jìn)行校驗(yàn)。
大家可以根據(jù)此方法驗(yàn)證自己的身份證號(hào)碼。
了解了身份證號(hào)碼的校驗(yàn)公式后,根據(jù)同余定理可證得身份證號(hào)碼校驗(yàn)碼的計(jì)算公式:
當(dāng)值等于10時(shí),用羅馬數(shù)字符X表示。此處需要注意:
是羅馬數(shù)字X,不應(yīng)理解為英文字母X
。
實(shí)際應(yīng)用
在金融行業(yè)軟件系統(tǒng)中,對(duì)于身份證號(hào)碼的采集、校驗(yàn)用途甚廣。
- 身份證號(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ī)制;
- 身份證號(hào)碼7到14位,可采集客戶的出生日期、年齡、生日;
- 身份證號(hào)碼17位,可采集客戶的性別,奇數(shù)表示男性,偶數(shù)表示女性;
- 身份證號(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整除,即得到一個(gè)整數(shù),那么就稱整數(shù)a與b對(duì)模m同余,記作
。
同余的性質(zhì)
- 反身性:
;
- 對(duì)稱性:若
,則
;
- 傳遞性:若
,
,則
;
- 同余式相加:若
,
,則
;
- 同余式相乘:若
,
,則
。
校驗(yàn)碼計(jì)算規(guī)則章節(jié)中,用到了以下公式
我們以此為例進(jìn)行證明,設(shè)
則
校驗(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)公式:
-
- 包括校驗(yàn)字符在內(nèi)的字符串的字符個(gè)數(shù);
-
- 表示
從右至左
包括校驗(yàn)碼字符在內(nèi)的字符的位置索引,即最右邊的字符;
-
- 表示第i位置上字符值;
-
- 基數(shù);
-
- 模數(shù)。
只不過(guò),使用一個(gè)校驗(yàn)字符的純系統(tǒng)與使用兩個(gè)校驗(yàn)字符的純系統(tǒng)在計(jì)算校驗(yàn)字符的方式上略有不同:
-
使用一個(gè)校驗(yàn)字符的純系統(tǒng)的校驗(yàn)字符計(jì)算公式
其中是校驗(yàn)字符,
也可以使用
替代,
。
-
使用兩個(gè)校驗(yàn)字符的純系統(tǒng)的校驗(yàn)字符計(jì)算公式
其中和
是校驗(yàn)字符,
也可以使用
替代,
。
純系統(tǒng)有兩種基本的計(jì)算方法,純系統(tǒng)遞歸法和純系統(tǒng)多項(xiàng)式法:
遞歸法
從左往右計(jì)算:
多項(xiàng)式法
使用公式表示:
其實(shí)遞歸法的計(jì)算完全展開(kāi),得到的也就是多項(xiàng)式法,所以兩種計(jì)算方法產(chǎn)生相同的結(jié)果。
混合系統(tǒng)
混合系統(tǒng)使用一個(gè)校驗(yàn)字符,遵守校驗(yàn)公式:
-
- 包括校驗(yàn)字符在內(nèi)的字符串的字符個(gè)數(shù);
-
- 表示
從右至左
包括校驗(yàn)碼字符在內(nèi)的字符的位置索引,即最右邊的字符;
-
- 表示第i位置上字符值;
-
和
- 兩個(gè)模數(shù);
-
- 除以
后的余數(shù),如果其值為0,則用
代替;
-
- 除以
后的余數(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);
}
}
}