了解身份证号码

了解身份证号码

身份证结构介绍

根据【中华人民共和国国家标准 GB 11643-1999】中有关身份号码的规定:身份号码是特征组合码,由十七位数字本体码和一位数字校验码组成。排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。

六位地址码的规则为:

  • 第一位数字表示地区

    1 表示华北、2 表示东北、3 表示华东、4 表示中南(华中、华南)、5 是西南、6 是西北、7 表示台湾、8 表示香港和澳门

  • 第二位数字表示户籍所在的直辖市、省、自治区

    华北:北京(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)

  • 第三位、第四位数字表示户籍所在的地区、县、县级市、旗

  • 第五位、第六位数字表示户籍所在地

八位出生日期码的规则为:第七位到第十四位表示编码对象的出生年、月、日,格式为 YYYYMMDD

15 位身份证号码的出生日期码只有 6 位,格式为 YYMMDD

三位顺序码的规则为:第十五位到第十七位为顺序码,对同年、月、日出生的人员编定的顺序号,其中第十七位奇数分给男性、偶数分给女性

一位校验码的规则为:身份证号码的最后一位为校验码

15 位身份证号码没有校验码

15 位身份证说明

早期的身份证是由 15 位数字本体码(六位地址码 + 六位出生日期码 + 三位顺序码)构成,六位出生日期码的格式为 YYMMDD,默认省略年份前两位 19(如:850631 表示 19850631)。后面考虑到千年虫的问题,从 15 位升级到 18 位。

身份证格式校验

身份证有效性校验一般分为三步:

  • 长度校验

    根据上面的介绍,身份证号码一般为 15 位或 18 位,通过正则进行判断。

    1
    2
    3
    4
    5
    6
    // 长度和普通格式校验
    const reg = /^[1-9]\d{5}(18|19|20)?\d{2}(0[1-9]|1[12])(0[1-9]|[12]\d|3[01])\d{3}(\d|X)$/i;

    if (!reg.test(cardNum)) {
    return { status: false, code: 1001, errMsg: '身份证号格式不符' };
    }
  • 地址码校验

    地址码为六位,后面四位(县以下)有可能会发生变更,具体变更可以查阅民政部的行政区划代码。因此,一般校验前两位地址码的有效性。

    1
    2
    3
    4
    5
    6
    // 校验前两位地址码
    const PROVINCE_CODE = { 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: "澳门" }

    if (!Object.keys(PROVINCE_CODE).includes(cardNum.slice(0, 2))) {
    return { status: false, code: 1002, errMsg: '身份证号码对应地区不存在' };
    }
  • 校验码校验

    校验码是按统一的公式计算的,具体计算规则如下:

    1. 将身份证号码前十七位数分别乘以不同的系数,一到十七位对应的系数为

      | | | | | | | | | | | | | | | | | | |
      | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- |
      | 位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
      | 系数 | 7 | 9 | 10 | 5 | 8 | 4 | 2 | 1 | 6 | 3 | 7 | 9 | 10 | 5 | 8 | 4 | 2 |

    2. 将十七位数字和系数相乘的结果相加

    3. 用累加的和除以 11,得到余数,并根据余数和校验码的换算表,得到校验码

      | 余数 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
      | —— | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- |
      | 校验码 | 1 | 0 | x | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 |

    如:某人身份证号位 53010219200508011x,首先计算前十七位的乘积和 [(5*7)+(3*9)+(0*10)+(1*5)+(0*8)+(2*4)+(1*2)+(9*1)+(2*6)+(0*3)+(0*7)+(5*9)+(0*10)+(8*5)+(0*8)+(1*4)+(1*2)] = 189;然后除以 11 得到余数 189 % 11 = 2,最后根据换算表得到校验码为 x,说明这个身份证号码是有效的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const FACTOR = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; // 1~17 位对应的系数
    const PARITY = [1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2]; // 余数和校验码的转换表

    let sum = 0;
    for (let i = 0; i < 17; i++) {
    sum += cardNum[i] * FACTOR[i];
    }

    if (!Object.is(`${PARITY[sum % 11]}`, `${cardNum[17]}`.toUpperCase())) {
    return { status: false, code: 1003, errMsg: '无效的身份证号' };
    }

汇总后的校验方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const validateIDCard = (cardNum) => {
// 长度和普通格式校验
const reg = /^[1-9]\d{5}(18|19|20)?\d{2}(0[1-9]|1[12])(0[1-9]|[12]\d|3[01])\d{3}(\d|X)$/i;
if (!reg.test(cardNum)) {
return { status: false, code: 1001, errMsg: '身份证号格式不符' };
}

// 校验前两位地址码
const PROVINCE_CODE = { 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: "澳门" }
if (!Object.keys(PROVINCE_CODE).includes(cardNum.slice(0, 2))) {
return { status: false, code: 1002, errMsg: '身份证号码对应地区不存在' };
}

const FACTOR = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; // 1~17 位对应的系数
const PARITY = [1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2]; // 余数和校验码的转换表

let sum = 0;
for (let i = 0; i < 17; i++) {
sum += cardNum[i] * FACTOR[i];
}

if (!Object.is(`${PARITY[sum % 11]}`, `${cardNum[17]}`.toUpperCase())) {
return { status: false, code: 1003, errMsg: '无效的身份证号' };
}

return { status: true, code: 1000, errMsg: '' };
};

15 位身份证号补全

了解了 15 位和 18 位身份证号码的构成后,就可以按照 18 位身份证号码的结构将 15 位身份证号码补全。

  1. 将 15 位身份证号码的出生年补全为四位,即加上省略的年份 19
  2. 将第一步得到的 17 位数分别乘以对应的系数,并将乘积进行累加
  3. 将累加的和除以 11 得到余数,然后根据余数与校验码的转换表获取对应的校验码
  4. 组合第一步的 17 位和第三步的校验码,得到最终的 18 位身份证号码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const IDCardCompetion = (cardNum) => {
// 长度和普通格式校验
const reg = /^[1-9]\d{5}\d{2}(0[1-9]|1[12])(0[1-9]|[12]\d|3[01])\d{3}$/i;
if (!reg.test(cardNum)) {
return { status: false, value: cardNum, code: 1001, errMsg: '身份证号格式不符' };
}

let value = `${cardNum.slice(0, 6)}19${cardNum.slice(6)}`; // 补全为 17 位

const FACTOR = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; // 1~17 位对应的系数
const PARITY = [1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2]; // 余数和校验码的转换表

let sum = 0;
for (let i = 0; i < 17; i++) {
sum += value[i] * FACTOR[i];
}

value = `${value}${PARITY[sum % 11]}`;

return { status: true, value, code: 1000, errMsg: '' };
};