{"action":"opened","issue":{"url":"https://api.github.com/repos/libin1991/libin_Blog/issues/397","repository_url":"https://api.github.com/repos/libin1991/libin_Blog","labels_url":"https://api.github.com/repos/libin1991/libin_Blog/issues/397/labels{/name}","comments_url":"https://api.github.com/repos/libin1991/libin_Blog/issues/397/comments","events_url":"https://api.github.com/repos/libin1991/libin_Blog/issues/397/events","html_url":"https://github.com/libin1991/libin_Blog/issues/397","id":305089253,"number":397,"title":"身份证号码的正则表达式Regex及验证详解","user":{"login":"libin1991","id":16753554,"avatar_url":"https://avatars1.githubusercontent.com/u/16753554?v=4","gravatar_id":"","url":"https://api.github.com/users/libin1991","html_url":"https://github.com/libin1991","followers_url":"https://api.github.com/users/libin1991/followers","following_url":"https://api.github.com/users/libin1991/following{/other_user}","gists_url":"https://api.github.com/users/libin1991/gists{/gist_id}","starred_url":"https://api.github.com/users/libin1991/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/libin1991/subscriptions","organizations_url":"https://api.github.com/users/libin1991/orgs","repos_url":"https://api.github.com/users/libin1991/repos","events_url":"https://api.github.com/users/libin1991/events{/privacy}","received_events_url":"https://api.github.com/users/libin1991/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-03-14T09:54:05Z","updated_at":"2018-03-14T09:54:05Z","closed_at":null,"author_association":"OWNER","body":"## 简言\r\n\r\n在做用户实名验证时，常会用到身份证号码的正则表达式及校验方案。本文列举了两种验证方案，大家可以根据自己的项目实际情况，选择适合的方案。\r\n![idcard-check](https://user-gold-cdn.xitu.io/2018/3/14/162238e3e11db257?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)\r\n## 身份证号码说明\r\n\r\n居民身份证号码，正确、正式的称谓应该是“公民身份号码”。根据【中华人民共和国国家标准 GB 11643-1999】中有关公民身份号码的规定，公民身份号码是特征组合码，由十七位数字本体码和一位数字校验码组成。排列顺序从左至右依次为：六位数字地址码，八位数字出生日期码，三位数字顺序码和一位数字校验码。\r\n\r\n以北京市朝阳区一女性身份证号码为例，身份证号码所表示的含义如下图所示：\r\n![身份证号码含义解析](https://user-gold-cdn.xitu.io/2018/3/14/162238e94fa233c8?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)\r\n注：该身份证号码来源于国标【GB 11643-1999】。\r\n\r\n下面我们就从零开始完成一个完整的身份证号码校验过程。\r\n\r\n## 方案1 （简单）\r\n\r\n### 1.1 分部规则\r\n\r\n我们首先提出方案1，并分步做如下规则定义：\r\n\r\n#### 1.1.1 地址码规则：\r\n\r\n- 地址码长6位\r\n- 以数字1-9开头\r\n- 后5位为0-9的数字\r\n\r\n根据以上规则，写出地址码的正则表达式： `/^[1-9]\\d{5}/`\r\n\r\n#### 1.1.2 年份码规则：\r\n\r\n- 年份码长4位\r\n- 以数字18，19或20开头\r\n- 剩余两位为0-9的数字\r\n\r\n根据以上规则，写出年份码的正则表达式： `/(18|19|20)\\d{2}/`。如果不需要18开头的年份，可以去掉18。\r\n\r\n#### 1.1.3 月份码规则：\r\n\r\n- 月份码长2位\r\n- 第一位数字为0，第二位数字为1-9\r\n- 或者第一位数字为1，第二位数字为0-2\r\n\r\n根据以上规则，写出月份码的正则表达式： `/((0[1-9])|(1[0-2]))/`。\r\n\r\n#### 1.1.4 日期码规则：\r\n\r\n- 日期码长2位\r\n- 第一位数字为0-2，第二位数字为1-9\r\n- 或者是10，20，30，31\r\n\r\n根据以上规则，写出日期码的正则表达式 ：`/(([0-2][1-9])|10|20|30|31)/`。\r\n\r\n#### 1.1.5 顺序码规则：\r\n\r\n- 顺序码长3位\r\n- 顺序码是数字\r\n\r\n根据以上规则，写出顺序码的正则表达式 ：`/\\d{3}/`。\r\n\r\n#### 1.1.6 校验码规则：\r\n\r\n- 校验码长1位\r\n- 可以是数字，字母x或字母X\r\n\r\n根据以上规则，写出校验码的正则表达式 ：`/[0-9Xx]/`。\r\n\r\n### 1.2 方案1正则表达式\r\n\r\n综合以上6条规则，给出完整的正则表达式及测试程序如下：\r\n\r\n    var p = /^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$/;\r\n    //输出 trueconsole.log(p.test(\"11010519491231002X\"));\r\n    //输出 false 不能以0开头console.log(p.test(\"01010519491231002X\"));\r\n    //输出 false 年份不能以17开头console.log(p.test(\"11010517491231002X\"));\r\n    //输出 false 月份不能为13console.log(p.test(\"11010519491331002X\"));\r\n    //输出 false 日期不能为32console.log(p.test(\"11010519491232002X\"));\r\n    //输出 false 不能以a结尾console.log(p.test(\"11010519491232002a\"));\r\n    \r\n\r\n[演示代码](https://link.juejin.im?target=http%3A%2F%2Fwww.42du.cn%2Frun%2F43)\r\n\r\n### 1.3 方案1分析\r\n\r\n方案1只是做了基本的格式判定，存在三个主要的不足：\r\n\r\n- 地址码判定不够精确。例：我国并不存在16，26开头的地区，却可通过验证\r\n- 日期判定不够精确。例:19490231也可通过验证，而2月并不存在31日\r\n- 校验码是由17位本体码计算得出，方案1并未校验此码\r\n\r\n## 方案2 （全面）\r\n\r\n根据方案1的不足，引入方案2进而改进方案1的不足。\r\n\r\n### 2.1 省级地址码校验\r\n\r\n华北：北京11，天津12，河北13，山西14，内蒙古15\r\n\r\n东北： 辽宁21，吉林22，黑龙江23\r\n\r\n华东： 上海31，江苏32，浙江33，安徽34，福建35，江西36，山东37\r\n\r\n华中： 河南41，湖北42，湖南43\r\n\r\n华南： 广东44，广西45，海南46\r\n\r\n西南： 四川51，贵州52，云南53，西藏54，重庆50\r\n\r\n西北： 陕西61，甘肃62，青海63，宁夏64，新疆65\r\n\r\n特别：台湾71，香港81，澳门82\r\n\r\n根据上述地址码做身份证号码的前两位校验，进一步的提高准确率。当前的地址码以2013版的行政区划代码【GB/T2260】为标准。由于区划代码的历史演变，使得地址码后四位校验变得不太可能。以三胖的身份证号为例，本人号码是2321开头，而当前行政区划代码表中并无此代码。因此本文只做前两位省级地址码的校验。\r\n\r\n也有说法表述91开头是外国人取得中国身份证号码的前两位编码，但本人并未得到证实。如有持91开头身份证或认识马布里的，请帮忙确认相关信息。\r\n\r\n#### 根据以上分析，给出省级地址码校验及测试程序如下：\r\n\r\n    var checkProv = function (val) {\r\n        var pattern = /^[1-9][0-9]/;\r\n        var provs = {11:\"北京\",12:\"天津\",13:\"河北\",14:\"山西\",15:\"内蒙古\",21:\"辽宁\",22:\"吉林\",23:\"黑龙江 \",31:\"上海\",32:\"江苏\",33:\"浙江\",34:\"安徽\",35:\"福建\",36:\"江西\",37:\"山东\",41:\"河南\",42:\"湖北 \",43:\"湖南\",44:\"广东\",45:\"广西\",46:\"海南\",50:\"重庆\",51:\"四川\",52:\"贵州\",53:\"云南\",54:\"西藏 \",61:\"陕西\",62:\"甘肃\",63:\"青海\",64:\"宁夏\",65:\"新疆\",71:\"台湾\",81:\"香港\",82:\"澳门\"};\r\n        if(pattern.test(val)) {\r\n            if(provs[val]) {\r\n                returntrue;\r\n            }\r\n        }\r\n        returnfalse;\r\n    }\r\n    //输出 true，37是山东console.log(checkProv(37));\r\n    //输出 false，16不存在console.log(checkProv(16));\r\n    \r\n\r\n[演示代码](https://link.juejin.im?target=http%3A%2F%2Fwww.42du.cn%2Frun%2F45)\r\n\r\n### 2.2 出生日期码校验\r\n\r\n#### 出生日期码的校验不做解释，直接给出如下函数及测试程序：\r\n\r\n    var checkDate = function (val) {\r\n        var pattern = /^(18|19|20)\\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)$/;\r\n        if(pattern.test(val)) {\r\n            var year = val.substring(0, 4);\r\n            var month = val.substring(4, 6);\r\n            var date = val.substring(6, 8);\r\n            var date2 = newDate(year+\"-\"+month+\"-\"+date);\r\n            if(date2 && date2.getMonth() == (parseInt(month) - 1)) {\r\n                returntrue;\r\n            }\r\n        }\r\n        returnfalse;\r\n    }\r\n    //输出 trueconsole.log(checkDate(\"20180212\"));\r\n    //输出 false 2月没有31日console.log(checkDate(\"20180231\"));\r\n    \r\n\r\n[演示代码](https://link.juejin.im?target=http%3A%2F%2Fwww.42du.cn%2Frun%2F44)\r\n\r\n### 2.3 校验码校验\r\n\r\n校验码的计算略复杂，先给出如下公式：\r\n![校验码公式](https://user-gold-cdn.xitu.io/2018/3/14/162238e94f9e1d87?imageView2/0/w/1280/h/960/format/webp/ignore-error/1)\r\n其中 ai 表示身份证本体码的第 i 位值，而 Wi 表示第 i 位的加权因子值。\r\n\r\n#### 加权因子表 【表1】：\r\ni12345678Wi791058421910111213141516176379105842\r\n#### X与校验码换算表 【表2】\r\nX12345678910a1810X9876543\r\n#### 算法过程：\r\n\r\n- 根据身份证主体码（前17位）分别与对应的加权因子（表1）计算乘积再求和，根据所得结果与11取模得到X值。\r\n- 根据 X 值查询表2，得出a18即校验码值。\r\n\r\n#### 校验码计算程序及测试见如下代码：\r\n\r\n    var checkCode = function (val) {\r\n        var p = /^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$/;\r\n        var factor = [ 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 ];\r\n        var parity = [ 1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2 ];\r\n        var code = val.substring(17);\r\n        if(p.test(val)) {\r\n            var sum = 0;\r\n            for(var i=0;i<17;i++) {\r\n                sum += val[i]*factor[i];\r\n            }\r\n            if(parity[sum % 11] == code.toUpperCase()) {\r\n                returntrue;\r\n            }\r\n        }\r\n        returnfalse;\r\n    }\r\n    // 输出 true， 校验码相符console.log(checkCode(\"11010519491231002X\"));\r\n    // 输出 false， 校验码不符console.log(checkCode(\"110105194912310021\"));\r\n    \r\n\r\n[演示代码](https://link.juejin.im?target=http%3A%2F%2Fwww.42du.cn%2Frun%2F46)\r\n\r\n### 2.4 方案2整体代码\r\n\r\n    var checkID = function (val) {\r\n        if(checkCode(val)) {\r\n            var date = val.substring(6,14);\r\n            if(checkDate(date)) {\r\n                if(checkProv(val.substring(0,2))) {\r\n                    returntrue;\r\n                }\r\n            }\r\n        }\r\n        returnfalse;\r\n    }\r\n    //输出 trueconsole.log(checkID(\"11010519491231002X\"));\r\n    //输出 false，校验码不符console.log(checkID(\"110105194912310021\"));\r\n    //输出 false，日期码不符console.log(checkID(\"110105194902310026\"));\r\n    //输出 false，地区码不符console.log(checkID(\"160105194912310029\"));\r\n    \r\n\r\n[演示代码](https://link.juejin.im?target=http%3A%2F%2Fwww.42du.cn%2Frun%2F47)\r\n\r\n以上为三胖对身份证号码验证的理解和分析，如有不足请大家予以指正。\r\n\r\n知名物理学家史蒂芬•霍金于2018年3月14日去世，享年76岁。一个博学又有趣的人，一路走好！\r\n\r\n世间再无霍金，时间永留简史！\r\n\r\n[原文地址](https://link.juejin.im?target=http%3A%2F%2Fwww.42du.cn%2Fp%2F41)"}}
{"actor":{"display_login":"libin1991"}}