js 校验身份证姓名及号码

3,524 阅读2分钟

js 校验身份证姓名及号码

最近在项目中遇到了关于身份证姓名和 id 的校验,特此记录。

姓名校验

首先明确校验规则,中国人的姓名,无非就是汉字+长度校验。这可很简单,随便去网上搜一下汉字校验,千篇一律的以下代码:

/^[\u4e00-\u9fa5]+$/

看起来好像没什么问题,实际测试好像也没什么问题。但是我们也知道,中国汉字是一直在增加的,对应的 unicode 编码也是一直在增加的。当然,我们可以手动把后续新增的 uncode 编码都加上,类似下面这样:

/^[\u4e00-\u9fa5\uF900-\uFA2D]+$/

但是这样也还是很繁琐,而且随着时间的推移需要不停的去维护这个函数。有没有什么更好的方式呢?

答案是有的

/^[\p{Unified_Ideograph}]$/u

其中\u正则表达式标志,表示正则表达式中的字符串按照 unicode 编码处理,\p则会根据 Unicode 属性进行匹配,后面需要跟着一个 Unicode 属性值,以这种格式\p{Unicode属性格式},由于 Unicode 属性值太多,这里不一一列举了,感兴趣的可以看这里, 这里我们使用Unified_Ideograph这个属性值,表示各种汉字字符。

ok, 大功告成了,现在我们不需要知道汉字的 unicode 编码范围也能匹配到汉字了,并且我们无需手动维护,这个正则是长久有效的。

兼容性

本身\p兼容性还是可以的,如果需要兼容更低版本的浏览器,可以手动配置 babel 的 plugin

plugins: [
  [
  	'@babel/plugin-proposal-unicode-property-regex',
  	{
  		useUnicodeFlag: false
  	}
 	]
]

这个 plugin 还提供了一个在线的demo,方便更直观的看到转义之后的结果。

身份证号校验

关于身份证号的验证比较简单,先去网上查一下身份证的组成规则。

针对二代身份证,一共 18 位,前面 6 位表示省市区,中间 8 位表示年月日,然后是 3 位随机数,最后一位是校验码。

对于一代身份证,一共 15 位,年份少两位,没有最后一位校验码。所以无非就是需要校验省市区,年月日和校验码。

详细规则参考百度百科

格式化

为了后续校验函数的统一,我们首先针对一代身份证做一个格式化

function normalId(id) {
  // 15位:年份只有后两位,没有校验码
  if (id.length === 15) {
    const id17 = id.substring(0, 6) + '19' + id.substring(6);
    return id17 + getCode(id17);
  }
}

我们在年份那里手动拼接上了19,然后手动计算出最后一位校验码拼接到末尾,这个getCode函数我们后面会提到,这里先不细说。

省市区校验

省市区一共有 6 位,前两位表示省级行政区,一共 34 个,简单观察了一下,还算有点规律,就简单校验了一下,至于市和区的校验,没什么校验的必要性。

function checkAddress(id: string): boolean {
  const re = /^1[1-5]|2[123]|3[1-7]|4[1-6]|5[0-4]|6[1-5]|71|81|82$/;
  return re.test(id.substring(0, 2));
}

出生年月日校验

这里主要校验两点,日期小于当前日期,并且是一个合法日期

function checkBirth(str: string): boolean {
  // 小于当前日期,并且日期合法
  const now = new Date();
  const [_, year, month, day] = /(\d{4})(\d{2})(\d{2})/.exec(str);
  const realDate = new Date(+year, +month - 1, +day);
  const realYear = realDate.getFullYear();
  const realMonth = realDate.getMonth() + 1;
  const realDay = realDate.getDate();
  return (
    realDate < now &&
    +year === realYear &&
    +month === realMonth &&
    +day === realDay
  );
}

校验码校验

这个校验码是由前面的 17 位通过某种计算的出来的,计算规则参考 👆 百度百科,我们只需要手动计算一下然后和用户输入的做一个对比即可:

// 每一位的加权数
const powerMap = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
// 余数对应的最后一位校验码
const codeMap = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];

// 计算正确的校验码
function getCode(str: string): string {
  let sum = 0;
  for (let i = 0; i < 17; i++) {
    sum += +str[i] * powerMap[i];
  }
  return codeMap[sum % 11];
}

// 对比校验码
function checkCode(id: string): boolean {
  const rightCode = getCode(id);
  return id.slice(-1) === rightCode;
}

大概就这些,感谢阅读,👦

参考资料

  1. jhuang.me/2018/01/26/…
  2. baike.baidu.com/item/%E5%B1…