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

更新日期: 2019-01-16阅读: 3.9k标签: 正则

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

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值

 

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

那么正确的验证如下

/**
 * @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: "上海",
    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: "澳门",
    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[idCard.substr(0, 2)]) {
    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;
}


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

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,
  ];
  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/
来自:https://www.cnblogs.com/starryqian/archive/2019/01/15/10272793.html


链接: https://www.fly63.com/article/detial/1827

javascript较全常用的表达正则验证,js中采用test()方法

正则表达式是一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。本文整理了JS较全且实用正则表达式。

js使用正则过滤emoji表情符号

手机端常常会遇到用户输入框,输入emoji,如果是数据库是UTF8,会遇到报错,原因是:UTF-8编码有可能是两个、三个、四个字节。Emoji表情是4个字节,而Mysql的utf8编码最多3个字节,所以数据插不进去。

一次记住js的6个正则方法

来记忆一些常用特殊字符,这个是正则本身的范畴了,是不是总觉得记不住?其实我也记不住,每次都是去搜索和online验证来完成一些任务。我也困恼过,其实最后还是因为自己写的少吧,唯手熟尔。。。

密码强度的正则表达式(JavaScript)总结

本文给出了两个密码强度的正则表达式方案,一个简单,一个更复杂和安全。并分别给出了两个方案的解析和测试程序。一般大家可以根据自己的项目的实际需要,自行定义自己的密码正则约定。

JavaScript判断字符串包含中文字符的方法总结

JS中判断一个字符串是否包含汉字,下面就介绍2中常用的实现方法:用正则表达式判断、用 Unicode 字符范围判断。

js常用正则表达式验证大全(整理详细且实用)

正则表达式对象用来规范一个规范的表达式,本文讲的是JS正则表达式大全(整理详细且实用),包括校验数字、字符、一些特殊的需求等等

javascript 正则表达式之分组与前瞻匹配详解

本文主要讲解javascript 的正则表达式中的分组匹配与前瞻匹配的,需要对正则的有基本认识。分组匹配:捕获性分组匹配、非捕获性分组匹配。前瞻匹配:正向前瞻匹配: (?=表达式) 后面一定要匹配有什么、反向前瞻匹配: (?!表达式) 后面一定不能要有什么

利用正则表达式去除所有html标签,只保留文字

后台将富文本编辑器中的内容返回到前端时如果带上了标签,这时就可以利用这种方法只保留文字。利用正则表达式去除所有html标签,只保留文字

正则表达式后行断言 • 探索 ES2018 和 ES2019

先后行断言(Lookaround Assertion)是正则表达式中的一个构造,明确了当前位置的前后字符序列,但没有其他副作用。当前 JavaScript 唯一支持的 Lookaround Assertion 是 先行断言,其匹配当前位置接下来的字符序列

循环下的正则匹配?说说正则中的lastIndex

正则有一个属性叫lastIndex,它表示正则下一次匹配时的起始位置。一般情况下我们是使用不到它的,但在正则中包含全局标志g时,正则的test和exec方法就会使用到它

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!