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;
}
完
大概就这些,感谢阅读,👦
参考资料