前言
根據(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ā)布 |
參考資料
- 中華人民共和國居民身份證法
- 中華人民共和國國家標(biāo)準(zhǔn) GB/T 2260-2007 中華人民共和國行政區(qū)劃代碼
- 中華人民共和國國家標(biāo)準(zhǔn) GB 11643-1999 公民身份證號碼
- 中華人民共和國國家標(biāo)準(zhǔn) GB/T 17710-1999 數(shù)據(jù)處理 校驗(yàn)碼系統(tǒng)
- 中華人民共和國國家標(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
身份證號碼的編碼規(guī)則
身份證號碼共18位,由17位本體碼和1位校驗(yàn)碼組成矾削。
- 前6位是地址碼壤玫,表示登記戶口時所在地的行政區(qū)劃代碼,依照《中華人民共和國行政區(qū)劃代碼》國家標(biāo)準(zhǔn)(GB/T2260)的規(guī)定執(zhí)行哼凯;
- 7到14位是出生年月日欲间,采用YYYYMMDD格式;
- 15到17位是順序碼断部,表示在同一地址碼所標(biāo)識的區(qū)域范圍內(nèi)猎贴,對同年、同月蝴光、同日出生的人編訂的順序號她渴,順序碼的奇數(shù)分配給男性,偶數(shù)分配給女性虱疏,即第17位奇數(shù)表示男性惹骂,偶數(shù)表示女性;
- 第18位是校驗(yàn)碼做瞪,采用ISO 7064:1983, MOD 11-2校驗(yàn)字符系統(tǒng)对粪,計(jì)算規(guī)則下一章節(jié)說明。
一代身份證與二代身份證的區(qū)別在于:
- 一代身份證是15位装蓬,二代身份證是18位著拭;
- 一代身份證出生年月日采用YYMMDD格式,二代身份證出生年月日采用YYYYMMDD格式牍帚;
- 一代身份證無校驗(yàn)碼儡遮,二代身份證有校驗(yàn)碼。
校驗(yàn)碼計(jì)算規(guī)則
身份證號碼中各個位置上的號碼字符值應(yīng)滿足下列公式的校驗(yàn):
-
表示號碼字符
從右至左
包括校驗(yàn)碼字符在內(nèi)的位置序號暗赶; - 表示第i位置上的號碼字符值鄙币,是身份證號碼第18位校驗(yàn)碼肃叶;
- 表示第i位置上的加權(quán)因子,加權(quán)因子計(jì)算公式:
舉例說明:
筆者身份證號碼為370683198901117657十嘿,根據(jù)上述公式進(jìn)行校驗(yàn)因惭。
大家可以根據(jù)此方法驗(yàn)證自己的身份證號碼。
了解了身份證號碼的校驗(yàn)公式后绩衷,根據(jù)同余定理可證得身份證號碼校驗(yàn)碼的計(jì)算公式:
當(dāng)值等于10時蹦魔,用羅馬數(shù)字符X表示。此處需要注意:是羅馬數(shù)字X咳燕,不應(yīng)理解為英文字母X
勿决。
實(shí)際應(yīng)用
在金融行業(yè)軟件系統(tǒng)中,對于身份證號碼的采集招盲、校驗(yàn)用途甚廣低缩。
- 身份證號碼前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ī)制万皿;
- 身份證號碼7到14位摧找,可采集客戶的出生日期、年齡牢硅、生日蹬耘;
- 身份證號碼17位,可采集客戶的性別减余,奇數(shù)表示男性综苔,偶數(shù)表示女性;
- 身份證號碼的驗(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整除,即得到一個整數(shù)创橄,那么就稱整數(shù)a與b對模m同余箩做,記作。
同余的性質(zhì)
- 反身性:妥畏;
- 對稱性:若邦邦,則;
- 傳遞性:若咖熟,圃酵,則;
- 同余式相加:若馍管,郭赐,則;
- 同余式相乘:若,捌锭,則俘陷。
校驗(yàn)碼計(jì)算規(guī)則章節(jié)中,用到了以下公式
我們以此為例進(jìn)行證明观谦,設(shè)
則
校驗(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)公式:
- - 包括校驗(yàn)字符在內(nèi)的字符串的字符個數(shù)瞒渠;
-
- 表示
從右至左
包括校驗(yàn)碼字符在內(nèi)的字符的位置索引徽龟,即最右邊的字符; - - 表示第i位置上字符值钦扭;
- - 基數(shù);
- - 模數(shù)凿菩。
只不過机杜,使用一個校驗(yàn)字符的純系統(tǒng)與使用兩個校驗(yàn)字符的純系統(tǒng)在計(jì)算校驗(yàn)字符的方式上略有不同:
-
使用一個校驗(yàn)字符的純系統(tǒng)的校驗(yàn)字符計(jì)算公式
其中是校驗(yàn)字符,也可以使用替代衅谷,椒拗。
-
使用兩個校驗(yàn)字符的純系統(tǒng)的校驗(yàn)字符計(jì)算公式
其中和是校驗(yàn)字符,也可以使用替代获黔,蚀苛。
純系統(tǒng)有兩種基本的計(jì)算方法,純系統(tǒng)遞歸法和純系統(tǒng)多項(xiàng)式法:
遞歸法
從左往右計(jì)算:
多項(xiàng)式法
使用公式表示:
其實(shí)遞歸法的計(jì)算完全展開玷氏,得到的也就是多項(xiàng)式法枉阵,所以兩種計(jì)算方法產(chǎn)生相同的結(jié)果。
混合系統(tǒng)
混合系統(tǒng)使用一個校驗(yàn)字符预茄,遵守校驗(yàn)公式:
- - 包括校驗(yàn)字符在內(nèi)的字符串的字符個數(shù);
-
- 表示
從右至左
包括校驗(yàn)碼字符在內(nèi)的字符的位置索引侦厚,即最右邊的字符耻陕; - - 表示第i位置上字符值;
- 和 - 兩個模數(shù)刨沦;
- - 除以后的余數(shù)诗宣,如果其值為0,則用代替想诅;
- - 除以后的余數(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);
}
}
}