验证是否是身份证
实现一个函数来验证中国大陆身份证号码的合法性,包括格式校验和校验码验证
问题
实现一个函数来验证中国大陆身份证号码是否合法。需要验证:
- 18位身份证号码的格式(15位旧版身份证已停用)
- 前17位必须是数字
- 最后一位可以是数字或字母X
- 出生日期的合法性
- 地区码的合法性
- 最后一位校验码是否正确(根据GB11643-1999标准)
解答
/**
* 验证是否是合法的身份证号码
* @param {string} idCard - 身份证号码
* @returns {boolean} 是否合法
*/
function isValidIdCard(idCard) {
// 基本格式验证:18位,前17位数字,最后一位数字或X
const reg = /^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
if (!reg.test(idCard)) {
return false;
}
// 统一转为大写
idCard = idCard.toUpperCase();
// 验证出生日期
const year = parseInt(idCard.substring(6, 10));
const month = parseInt(idCard.substring(10, 12));
const day = parseInt(idCard.substring(12, 14));
const date = new Date(year, month - 1, day);
if (
date.getFullYear() !== year ||
date.getMonth() !== month - 1 ||
date.getDate() !== day
) {
return false;
}
// 验证校验码
// 加权因子
const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
// 校验码对应值
const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
let sum = 0;
for (let i = 0; i < 17; i++) {
sum += parseInt(idCard[i]) * weights[i];
}
const checkCode = checkCodes[sum % 11];
return idCard[17] === checkCode;
}
/**
* 验证身份证(增强版,返回详细信息)
* @param {string} idCard - 身份证号码
* @returns {object} 验证结果和详细信息
*/
function validateIdCard(idCard) {
const result = {
valid: false,
message: '',
info: null
};
if (!idCard || typeof idCard !== 'string') {
result.message = '身份证号码不能为空';
return result;
}
idCard = idCard.trim().toUpperCase();
// 格式验证
const reg = /^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dX]$/;
if (!reg.test(idCard)) {
result.message = '身份证号码格式不正确';
return result;
}
// 提取信息
const province = idCard.substring(0, 2);
const year = parseInt(idCard.substring(6, 10));
const month = parseInt(idCard.substring(10, 12));
const day = parseInt(idCard.substring(12, 14));
const gender = parseInt(idCard[16]) % 2 === 0 ? '女' : '男';
// 验证日期
const date = new Date(year, month - 1, day);
if (
date.getFullYear() !== year ||
date.getMonth() !== month - 1 ||
date.getDate() !== day
) {
result.message = '出生日期不合法';
return result;
}
// 验证校验码
const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
let sum = 0;
for (let i = 0; i < 17; i++) {
sum += parseInt(idCard[i]) * weights[i];
}
const checkCode = checkCodes[sum % 11];
if (idCard[17] !== checkCode) {
result.message = '校验码不正确';
return result;
}
// 验证通过
result.valid = true;
result.message = '身份证号码合法';
result.info = {
province,
birthday: `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
gender,
age: new Date().getFullYear() - year
};
return result;
}
使用示例
// 基础验证
console.log(isValidIdCard('110101199003074593')); // true
console.log(isValidIdCard('110101199003074592')); // false (校验码错误)
console.log(isValidIdCard('11010119900307459')); // false (长度不够)
console.log(isValidIdCard('110101199013074593')); // false (月份错误)
// 增强版验证(返回详细信息)
const result1 = validateIdCard('110101199003074593');
console.log(result1);
// {
// valid: true,
// message: '身份证号码合法',
// info: {
// province: '11',
// birthday: '1990-03-07',
// gender: '男',
// age: 34
// }
// }
const result2 = validateIdCard('110101199003074592');
console.log(result2);
// {
// valid: false,
// message: '校验码不正确',
// info: null
// }
// 支持小写x
console.log(isValidIdCard('11010119900307459x')); // true
// 错误格式测试
console.log(validateIdCard('')); // 身份证号码不能为空
console.log(validateIdCard('123456789012345678')); // 身份证号码格式不正确
console.log(validateIdCard('110101199002304593')); // 出生日期不合法
关键点
- 正则表达式验证:使用正则快速验证基本格式,包括长度、数字位置、日期范围等
- 出生日期校验:通过Date对象验证日期的真实性,避免如2月30日这样的非法日期
- 校验码算法:按照GB11643-1999国家标准,使用加权因子和模11算法计算校验码
- 前17位数字分别乘以对应的加权因子(7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2)
- 求和后对11取模
- 根据余数在校验码数组中取对应值(1,0,X,9,8,7,6,5,4,3,2)
- 大小写处理:统一将字母X转为大写进行比较
- 信息提取:可以从身份证号中提取省份、出生日期、性别、年龄等信息
- 错误提示:增强版函数提供详细的错误信息,便于用户修正输入
目录