winform之身份证验证实例
一、15位身份证和18位身份证号码结构介绍
只有15位号码的就是第一代身份证,而18位号码的则是第二代身份证。15位的身份证和18位的身份证的区别是:第二代身份证比第一代身份证增加了出生年份中的前两位数字和最后一位数字校验码。
18位的第二代身份证号码是特征组合码,由十七位数字本体码和一位校验码组成。排列顺序从左至右依次为:六位数字地址码、八位数字出生日期码、三位数字顺序码和一位数字校验码。
地址码(前六位数):表示身份证编码对象常住户口所在省份、地市,以及县(市、旗、区)的行政区划代码,按照GB/T2260的规定执行。其中:前1、2位数字表示所在省份的代码; 第3、4位数字表示所在地市的代码; 第5、6位数字表示:所在县(市、旗、区)的代码。
出生日期码(第七位至十四位):表示身份证编码对象出生的年、月、日,按GB/T7408的规定执行,年、月、日代码之间不用分隔符。
顺序码(第十五位至十七位):表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。
校验码(第十八位数):作为尾号的校验码,是由身份证号码编制单位按统一的公式计算出来的,如果某人的尾号是0-9,都不会出现Ⅹ,但如果尾号是10,那么就得用Ⅹ来代替,因为如果用10做尾号,那么此人的身份证就变成了19位,而19位的号码违反了国家标准,并且我国的计算机应用系统也不承认19位的身份证号码。Ⅹ是罗马数字的10,用Ⅹ来代替10,可以保证公民的身份证符合国家标准。
要进行身份证号码的验证,首先需要了解我国身份证号码的编码规则。我国身份证号码多由若干位数字或者数字与字母混合组成。早期身份证由15位数字构成,这主要是在1980年以前发放的身份证,后来考虑到千年虫问题,因为15位的身份证号码只能为1900年1月1日到1999年12月31日出生的人编号,所以又增加了18位身份证号码编号规则。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
A A A A A A Y Y M M D D N N S
前六位AAAAAA是身份证编码对象的所在地(出生地)的编码,该号码可由国家统计局公布的相关标准中得到。YY表示出生年的后两位,MM和DD表示出生月和日,不足两位的高位补0,NNS为顺序号,无法确定。S为性别识别码,男性为奇数,女性为偶数。了解了这些,再来写代码就变得容易多了。
二、算法实现
了解了身份证号码的规则后,我们就可以推断出,身份证的15位转化位需要两步。首先把15位身份证号补全为17位,然后再补全最后一位。但是最后一位是数字还是字母X?这里又出现了问题。我们知道,身份证的最后一位为校验位,那么最后一位是怎么得到的呢?原来,最后一位是由数字1-9组成,超过9的比如11就用字母X表示,否则号码就变成了19位。了解了这些,经过整理得出身份证补全算法实现思想如下:
step1、将15位身份证号码加入出生年变为17位
step2、将step1得到的身份证17位数分别乘以不同的系数。从第1位到第17位的系数分别为:7-9-10-5-8-4-2-1-6-3-7-9-10-5-8-4-2.
step3、将这17位数字和系数相乘的结果相加
step4、将step3的结果除以11,得出余数
由于数字的特殊性,这些余数只可能是0-10这11个数字,身份证最后一位的对应数字为1-0-X-9-8-7-6-5-4-3-2.。例上面的余数结果为3那么对应身份证号码的最后一位就是9,如果是10,身份证最后一位便是2。
三、实例解析
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Windows.Forms; 10 11 namespace formEx1 12 { 13 public partial class FormEx : Form 14 { 15 public FormEx() 16 { 17 InitializeComponent(); 18 } 19 20 private void btnPic_Click(object sender, EventArgs e) 21 { 22 //身份算法判定 身份证有15位和18位 23 //15位身份证组成: 省(2)市(2)县(2)年(2)月(2)日(2)+顺序号+检验位 24 int age = 0; 25 int year = 9; 26 string idNo = txtIdNo.Text.Trim(); 27 if (idNo.Length==15) 28 { 29 year=Convert.ToInt32(idNo.Substring(6,2))+1900; 30 age = DateTime.Now.Year - year; 31 if (age>18) 32 { 33 picBox.Visible = true; 34 } 35 else 36 { 37 MessageBox.Show("年龄小于18,不适宜看此图片"); 38 } 39 } 40 else if (idNo.Length==18) 41 { 42 if (!this.CheckIdNo(idNo)) 43 { 44 MessageBox.Show("身份证号码有误,请检查后重新输入"); 45 return; 46 } 47 year = Convert.ToInt32(idNo.Substring(6,4)); 48 age = DateTime.Now.Year - year; 49 if (age > 18) 50 { 51 picBox.Visible = true; 52 } 53 else 54 { 55 MessageBox.Show("年龄小于18,不适宜看此图片"); 56 } 57 } 58 else 59 { 60 MessageBox.Show("身份证号码必须是18位,请确认后再重新查看"); 61 } 62 } 63 private bool CheckIdNo(string id) 64 { 65 int[] wi = { 7 , 9 , 10 , 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5 ,8 ,4 ,2 }; 66 string checkNum = "10X98765432"; 67 string num17 = id.Substring(0,17); 68 string num18 = id.Substring(17); 69 int sum = 0; 70 for (int i = 0; i < 17; i++) 71 { 72 sum =sum+ Convert.ToInt32( num17[i].ToString())*wi[i] ; 73 } 74 int check = sum % 11; 75 string result = checkNum[check].ToString(); 76 if (num18.Equals(result, StringComparison.OrdinalIgnoreCase)) 77 { 78 return true; 79 } 80 else 81 { 82 return false; 83 } 84 85 } 86 private void txtIdNo_KeyPress(object sender, KeyPressEventArgs e) 87 { 88 //输入数字以为其他的阻止输入 89 if (e.KeyChar<\'0\'||e.KeyChar>\'9\') 90 { 91 e.Handled = true; 92 } 93 //如果第18位是x,则不阻输入,如果最后一位是其他字符,则阻止用户输入 94 if (txtIdNo.SelectionStart==17&&(e.KeyChar==\'x\'||e.KeyChar==\'X\')) 95 { 96 e.Handled = false; 97 } 98 //空格键 99 if (e.KeyChar==8) 100 { 101 e.Handled = false; 102 } 103 } 104 } 105 }