今天我研究了下身份证号码的校验位,总结以下。ide
根据中华人民共和国国家标准《GB 11643-1999 公民身份号码(Citizen identification number)》,咱们的身份证号由18位数字组成,其中前17个数字是本体码(master number),最后一个数字是校验码(check number),校验码是根据本体码的17个数字计算而得的。函数
(图片摘自《GB 11643-1999》)spa
在前面17个数字组成的本体码中,最开始的6位是地址码,是由《GB/T 2260 中华人民共和国行政区划代码》规定的,如北京市朝阳区是110105;中间8位数字表明出生日期,前面4位表明年,中间2位表明月,后面2位表明日,如1991年9月20日会被编为19910920;最后3位数字是一个顺序码,顺序码的奇数分配给男性,偶数分配给女性。最后1位是校验码,也就是咱们后面要写的内容。code
校验码采用的是国际标准化组织ISO订立的《ISO 7064: 1983》中的“MOD 11-2”校验码系统。图片
身份证号码一共18位,从右向左被依次编号为一、二、三、四、……、18,如今为各位都设置一个权(weight),用W表示,编号为i的数字权为:W[i]=2^(i-1) (mod 11)string
如:W[1]=2^0%11=1;W[2]=2^1%11=2;等等it
编号 | 权重 | 编号 | 权重 | 编号 | 权重 |
1 | 1 | 7 | 9 | 13 | 4 |
2 | 2 | 8 | 7 | 14 | 8 |
3 | 4 | 9 | 3 | 15 | 5 |
4 | 8 | 10 | 6 | 16 | 10 |
5 | 5 | 11 | 1 | 17 | 9 |
6 | 10 | 12 | 2 | 18 | 7 |
校验公式为:io
其中a[i]表明身份证号上第i位数字,W[i]表明第i位数字的权table
由于W[1]的值为1,因此公式又能够写成:ast
由于a[2]到a[18]即身份证自左向右的前17个数字,是已知的,每一位的权也是已知的,所以能够经过上面这个公式计算出a[1],这个a[1]是身份证号码中最右侧的数字,也就是校验码。
《GB 11643-1999》给出了一个后面大Sigma符号中表达式(下表中用S表示)与校验位a[1]的一一对应关系:
S值 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
校验 | 1 | 0 | X | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 |
由于除法在除数为11时有可能余数为10,10是两位数,所以用罗马数字中表明10的“X”代替。
如下代码是用C#写的,先添加要用到的命名空间:
using System; using System.Text.RegularExpressions;
函数:根据本体码计算校验码
/// <summary> /// 根据本体码计算校验码 /// </summary> /// <param name="sMasterNumber">本体码(17位)</param> /// <returns>校验码</returns> static char CalcCheckNumber(string sMasterNumber) { //本体码必须为17位且所有应为数字 if (sMasterNumber.Length != 17) { Console.WriteLine("错误:本体码必须为17位!"); return ' '; } if (!Regex.IsMatch(sMasterNumber, @"^\d*$")) { Console.WriteLine("错误:本体码中全部位都应为数字!"); return ' '; } //身份证号码各位的权 int[] weight = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, 1 }; //计算校验位 int[] id = new int[18]; for (int i = 0; i < 17; i++) { id[i] = (int)(sMasterNumber[i] - '0'); } int temp = 0; for (int i = 0; i < 17; i++) { temp += id[i] * weight[i]; } temp = temp % 11; switch (temp) { case 0: return '1'; case 1: return '0'; case 2: return 'X'; case 3: return '9'; case 4: return '8'; case 5: return '7'; case 6: return '6'; case 7: return '5'; case 8: return '4'; case 9: return '3'; case 10: return '2'; default: return ' '; } }
下面的Main函数中,给出了两个身份证号码的本体码,分别求出它们完整的身份证号:
static void Main(string[] args) { string sMaster1 = "11010519491231002"; char cCheck1 = CalcCheckNumber(sMaster1); Console.WriteLine("本体码:" + sMaster1); Console.WriteLine("校验码:" + cCheck1); Console.WriteLine("身份证号码:" + sMaster1 + cCheck1); Console.WriteLine("----------"); string sMaster2 = "44052418800101001"; char cCheck2 = CalcCheckNumber(sMaster2); Console.WriteLine("本体码:" + sMaster2); Console.WriteLine("校验码:" + cCheck2); Console.WriteLine("身份证号码:" + sMaster2 + cCheck2); Console.WriteLine("----------"); Console.Write("按任意键继续 ..."); Console.ReadKey(true); }
运行结果截图:
在计算机判断身份证号码合理性的时候,有下面几点须要考察:
行政区划代码是否存在?
出生日期是否合理?
校验位数值是否正确?
另外,若是用户还填写过其余信息,好比出生日期、性别等,还能够检查这些项与身份证号码是否一致。
下面这个C#函数,输入一个18位身份证号码,返回校验位数值是否正确:
/// <summary> /// 判断身份证号校验位是否正确 /// </summary> /// <param name="IDNumber"></param> /// <returns></returns> static bool IsLegalCheckNumber(string sIDNumber) { //身份证号码必须为18位,前17个数字必须为数字,最后一个数字必须为数字或字母X if (sIDNumber.Length != 18) { Console.WriteLine("错误:身份证号码必须为18位!"); return false; } if (!Regex.IsMatch(sIDNumber, @"^\d{18}|\d{17}X$")) { Console.WriteLine( "错误:身份证号码前17个数字必须为数字," + "最后一个数字必须为数字或字母X!"); return false; } //判断校验位是否合规 char check = CalcCheckNumber(sIDNumber.Substring(0, 17)); if (check == sIDNumber[17]) { return true; } return false; }
下面的Main函数中,给出了两个身份证号码,分别判断它们的校验位是否正确:
static void Main(string[] args) { string sID1 = "440524188001010014"; bool bIsLegal1 = IsLegalCheckNumber(sID1); Console.WriteLine("身份证号:" + sID1); Console.Write("校验结果:"); Console.WriteLine(bIsLegal1 == true ? "合规" : "不合规"); Console.WriteLine("----------"); string sID2 = "44052418800101001X"; bool bIsLegal2 = IsLegalCheckNumber(sID2); Console.WriteLine("身份证号:" + sID2); Console.Write("校验结果:"); Console.WriteLine(bIsLegal2 == true ? "合规" : "不合规"); Console.WriteLine("----------"); Console.Write("按任意键继续 ..."); Console.ReadKey(true); }
运行结果截图:
END