Excel 身份证号码合法性校验函数说明及其代码逻辑
安装与使用
工具是一个ExcelDNA的 xll 加载项,如下图,32位Excel装前者,64位装后者。
64位Excel加载如图(还可以配合ExcelDNA的函数参数提示支持xll,这里不做说明)
在【公式】—【函数】可以找到IDCardFuns类下的IDNumCheck函数,用法是=IDNumCheck(身份证号码,性别)
实现逻辑与代码
身份证号码合法性校验的一般逻辑有:
①位数
18位没得商量;
②性别
一般校验时会有先期录入的性别(“男” or “女”),与通过身份证号码计算出来的性别等于比较;
③出生年月日
a.年龄 是当前计算机系统的年份与身份证年份的差值,大于100判断为错误(这是一个怀疑错误),身份证年份大于系统年份也判断为错误;
b.月份 必须在[1,12]区间内,多了 少了都不对;
c.日期 1.3.5.7.8.10.12这几个31天没得跑,不在[1,31]是不对的,4.6.9.11是[1,30],2月的更复杂了,还得去分平闰年,区间为[1,28]或[1,29]。
④校验码
这个东西应该不多见,但必要。
二代身份证号码的前17位数字是可以通过一个算法,算出第18位的,第18位就叫做校验码。
整个代码贴到下面
public class IDCardFuncs
{
[ExcelFunction(Name = "GetGenderFromIDNum", Description = "18位身份证号码提取性别,号码不是18位返回错误提示。", Category = "IDCardFuncs", IsMacroType = true)]
public static string GetGenderFromIDNum(string idNumber)
{
if (idNumber.Length == 18)
{
int num;
if (int.TryParse(idNumber.Substring(16, 1), out num))
{
return (num % 2) switch
{
1 => "男",
0 => "女",
_ => ""
};
}
else
{ return "身份证号码第16位不是数值"; }
}
else
{
return "身份证号码不为18位";
}
}
[ExcelFunction(Name = "IDNumCheck", Description = "18位身份证号码合法性检查", Category = "IDCardFuncs", IsMacroType = true)]
public static string IDNumCheck(string idNum, string gender)
{
if (idNum.Length != 18)
return "身份证号码应为18位";
else if (!GenderCheck(idNum, gender))
return "性别与身份证号码不匹配";
else if (!DateCheck(idNum))
return "身份证号码出生日期有误";
else if (CodeCheck(idNum) != idNum.Substring(17, 1).ToUpper())
return $"校验码不正确,最后一位是{CodeCheck(idNum)}";
else
return "True";
}
private static bool GenderCheck(string idNum, string gender)
{
return IDCardFuncs.GetGenderFromIDNum(idNum) == gender;
}
private static bool DateCheck(string idNum)
{
bool dateBool = true;
int year = int.Parse(idNum.Substring(6, 4));
int month = int.Parse(idNum.Substring(10, 2));
int date = int.Parse(idNum.Substring(12, 2));
//年份大于系统时间或者小于当前年份的前100年(大于100岁)判为错
if (year > System.DateTime.Now.Year || year < System.DateTime.Now.Year - 100 || month < 1 || month > 12) dateBool = false;
switch (month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
if (date == 0 || date > 31)
dateBool = false;
break;
case 2:
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
{
if (date == 0 || date > 29)
dateBool = false;
}
else
{
if (date == 0 || date > 28)
dateBool = false;
}
break;
default:
if (date == 0 || date > 30)
dateBool = false;
break;
}
return dateBool;
}
private static string CodeCheck(string idNum)
{
//变量名懒得想,用了奇葩命名法。
int[] 数字 = new int[idNum.Length];
for (int i = 0; i < idNum.Length - 1; i++) //转身份证号码字符串前17位为int型数组
{ 数字[i] = int.Parse(idNum.Substring(i, 1)); }
int[] 系数 = new int[] { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
int 前17位与系数乘积之和 = 0;
for (int i = 0; i < 17; i++)
{ 前17位与系数乘积之和 = 前17位与系数乘积之和 + 数字[i] * 系数[i]; }
int 余数 = 前17位与系数乘积之和 % 11;
return 余数 switch
{
0 => "1",
1 => "0",
2 => "X",
3 => "9",
4 => "8",
5 => "7",
6 => "6",
7 => "5",
8 => "4",
9 => "3",
10 => "2",
_ => ""
};
}
}