欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

正则匹配身份证有bug你知道么?

程序员文章站 2022-04-13 15:56:47
在开发中,我们需要验证用户的输入信息,多半采用正则验证,下面就是身份证证号的几种常用的正则表达式: var reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/; var reg= /^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2] ......

在开发中,我们需要验证用户的输入信息,多半采用正则验证,下面就是身份证证号的几种常用的正则表达式:

var  reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|x|x)$)/;

var reg= /^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$/;

var  reg = /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{4}$/;

但是这些并不能管用,是不是很气人?

这是为什么呢?

下面我们看一下身份证的规则

身份证查询系统说明:

输入准确的18位身份证号码即可查询身份证号码归属地,年龄,性别,通过身份证号码查询姓名。

输入不合法格式的身份证号码会提示身份证号码错误,本身份证号码查询系统也可作为身份证号码验证。

身份证号码和姓名格式科普:前1-6位为行政区划代码即归属地,第7-14位为出生年月日,第15-17位为顺序代码,在同一个地区出生同一个出生的人通过顺序号码区分,第17位奇数表示男性,偶数表示女性,第18位为校验码,用于校验身份证号码是否合法

 

很显然我们正则验证出错的原因就是第18位,用于身份证号是否合法验证的校验

 

这是为什么呢?是不是很诡异?按照道理讲,不应该不符合就是false?但是返回的都是true;

因为是这样的,我们使用的正则表达式 reg.test(''),当我们使用test其实就是通过我们写的正则表达式去动态匹配我们输入的字符串是否符合我们表达式的要求,如果符合就会返回boolean值

 

这就是为什么我们身份证验证会出错,因为我们正则只是匹配了形式,并没有按照身份证的规则去动态验证,没有权重

那么正确的验证如下

 1 /**
 2  * @params  idcard  string
 3  * @outparams res
 4  *  status : boolean
 5  *  msg : string
 6  *
 7  */
 8 function checkidcard(idcard) {
 9   idcard = idcard.tostring();
10   var city = {
11     11: "北京",
12     12: "天津",
13     13: "河北",
14     14: "山西",
15     15: "内蒙古",
16     21: "辽宁",
17     22: "吉林",
18     23: "黑龙江 ",
19     31: "上海",
20     32: "江苏",
21     33: "浙江",
22     34: "安徽",
23     35: "福建",
24     36: "江西",
25     37: "山东",
26     41: "河南",
27     42: "湖北 ",
28     43: "湖南",
29     44: "广东",
30     45: "广西",
31     46: "海南",
32     50: "重庆",
33     51: "四川",
34     52: "贵州",
35     53: "云南",
36     54: "* ",
37     61: "陕西",
38     62: "甘肃",
39     63: "青海",
40     64: "宁夏",
41     65: "*",
42     71: "*",
43     81: "香港",
44     82: "澳门",
45     91: "国外 "
46   };
47   var tip = "";
48   var pass = true;
49 
50   if (
51     !idcard ||
52     !/^\d{6}(18|19|20)?\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|x)$/i.test(
53       idcard
54     )
55   ) {
56     tip = "身份证号格式错误";
57     pass = false;
58   } else if (!city[idcard.substr(0, 2)]) {
59     tip = "地址编码错误";
60     pass = false;
61   } else {
62     //18位身份证需要验证最后一位校验位
63     if (idcard.length == 18) {
64       idcard = idcard.split("");
65       //∑(ai×wi)(mod 11)
66       //加权因子
67       var factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
68       //校验位
69       var parity = [1, 0, "x", 9, 8, 7, 6, 5, 4, 3, 2];
70       var sum = 0;
71       var ai = 0;
72       var wi = 0;
73       for (var i = 0; i < 17; i++) {
74         ai = idcard[i];
75         wi = factor[i];
76         sum += ai * wi;
77       }
78       var last = parity[sum % 11];
79       if (parity[sum % 11] != idcard[17]) {
80         tip = "校验位错误";
81         pass = false;
82       }
83     }
84   }
85   var obj = {
86     status: pass,
87     msg: tip
88   };
89   if (!pass) {
90     return obj;
91   }
92 
93   return obj;
94 }

写法注意事项:省区编码,我们可以写成数组的形式,但是需要去循环操作,会比较消耗性能:如果是数组,新手是这么写的

function checkidcard(idcard) {
  idcard = idcard.tostring();
  var city = [
    11,
    12,
    13,
    14,
    15,
    21,
    22,
    23,
    31,
    33,
    34,
    35,
    36,
    37,
    41,
    42,
    43,
    44,
    45,
    46,
    50,
    51,
    52,
    54,
    61,
    62,
    63,
    64,
    65,
    71,
    81,
    91
  ];
  var tip = "";
  var pass = true;
  var flag = true;
  for(var i=0;i<city.length;i++){
    if (city[i] == idcard.substr(0, 2)) falg= false;
  }
  if (
    !idcard ||
    !/^\d{6}(18|19|20)?\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|x)$/i.test(
      idcard
    )
  ) {
    tip = "身份证号格式错误";
    pass = false;
  } else if (!flag) {
           tip = "地址编码错误";
           pass = false;
         } else {
           //18位身份证需要验证最后一位校验位
           if (idcard.length == 18) {
             idcard = idcard.split("");
             //∑(ai×wi)(mod 11)
             //加权因子
             var factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
             //校验位
             var parity = [1, 0, "x", 9, 8, 7, 6, 5, 4, 3, 2];
             var sum = 0;
             var ai = 0;
             var wi = 0;
             for (var i = 0; i < 17; i++) {
               ai = idcard[i];
               wi = factor[i];
               sum += ai * wi;
             }
             var last = parity[sum % 11];
             if (parity[sum % 11] != idcard[17]) {
               tip = "校验位错误";
               pass = false;
             }
           }
         }
  var obj = {
    status: pass,
    msg: tip
  };
  if (!pass) {
    return obj;
  }

  return obj;
}

  优化上面的代码

/**
 * @params  idcard  string
 * @outparams res
 *  status : boolean
 *  msg : string
 *
 */
function checkidcard(idcard) {
  idcard = idcard.tostring();
  var city = [
    11,
    12,
    13,
    14,
    15,
    21,
    22,
    23,
    31,
    33,
    34,
    35,
    36,
    37,
    41,
    42,
    43,
    44,
    45,
    46,
    50,
    51,
    52,
    54,
    61,
    62,
    63,
    64,
    65,
    71,
    81,
    91
  ];
  var tip = "";
  var pass = true;

  if (
    !idcard ||
    !/^\d{6}(18|19|20)?\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|x)$/i.test(
      idcard
    )
  ) {
    tip = "身份证号格式错误";
    pass = false;
  } else if (city.indexof(idcard.substr(0, 2)) < 0) {
    tip = "地址编码错误";
    pass = false;
  } else {
    //18位身份证需要验证最后一位校验位
    if (idcard.length == 18) {
      idcard = idcard.split("");
      //∑(ai×wi)(mod 11)
      //加权因子
      var factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
      //校验位
      var parity = [1, 0, "x", 9, 8, 7, 6, 5, 4, 3, 2];
      var sum = 0;
      var ai = 0;
      var wi = 0;
      for (var i = 0; i < 17; i++) {
        ai = idcard[i];
        wi = factor[i];
        sum += ai * wi;
      }
      var last = parity[sum % 11];
      if (parity[sum % 11] != idcard[17]) {
        tip = "校验位错误";
        pass = false;
      }
    }
  }
  var obj = {
    status: pass,
    msg: tip
  };
  if (!pass) {
    return obj;
  }

  return obj;
}

注意一下写法,尽量减少循环的操作,推荐使用第一种和第三种用法;如果你是es6用户,那么建议var 修改成 let 

最后基于模块的思想,建议搭建可以在实际开发中,把验证的方法统一一个对象去处理,

然后哪里需要哪里引入。

参考博文:https://blog.csdn.net/a632202838/article/details/44827427

在线身份查询:http://sfz.diqibu.com/