题目内容:
18位身份证标准在国家质量技术监督局于1999年7月1日实施的 GB11643-1999《公民身份号码》中做了明确的规定。 GB11643-1999《公民身份号码》为GB11643-1989《社会保障号码》的修订版,其中指出将原标准名称"社会保障号码"更名为"公民身份 号码",另外GB11643-1999《公民身份号码》从实施之日起代替GB11643-1989。GB11643-1999《公民身份号码》主要内容如 下:
一、范围
该标准规定了公民身份号码的编码对象、号码的结构和表现形式,使每个编码对象获得一个唯一的、不变的法定号码。
二、编码对象
公民身份号码的编码对象是具有中华人民共和国国籍的公民。
三、号码的结构和表示形式
1、号码的结构
公民身份号码是特征组合码,由十七位数字本体码和一位校验码组成。排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。
2、地址码
表示编码对象常住户口所在县(市、旗、区)的行政区划代码,按GB/T2260的规定执行。
3、出生日期码
表示编码对象出生的年、月、日,按GB/T7408的规定执行,年、月、日代码之间不用分隔符。
4、顺序码
表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。
5、校验码
(1)十七位数字本体码加权求和公式
S = Sum(Ai * Wi), i = 0, ... , 16 ,先对前17位数字的权求和
Ai: 表示第i位置上的身份证号码数字值
Wi: 表示第i位置上的加权因子
Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
(2)计算模
Y = mod(S, 11)
(3)通过模得到对应的校验码
Y: 0 1 2 3 4 5 6 7 8 9 10
校验码: 1 0 X 9 8 7 6 5 4 3 2
四、举例如下:
北京市朝阳区: 11010519491231002X
广东省汕头市: 440524188001010014
15位的身份证号升级办法:
15位的身份证号:dddddd yymmdd xx p
18位的身份证号:dddddd yyyymmdd xx p y
-
其中dddddd为地址码(省地县三级)
-
yyyymmdd yymmdd 为出生年月日
-
xx顺号类编码
-
p性别
15 位的 yy 年升为 18 位后,变成 19yy年,但对于百岁以上老人, 则为 18yy 年,此时,他们的最后三位顺序码为996, 997, 998 或 999 来标记。
输入格式:
输入n组身份证号码,第一行为个数,以后每行为身份证号码。
输出格式:
如果输入的身份证号码为15位,则将其升级为18位后显示输出;否则判断其是否为合法身份证号,并逐行输出。
输入样例:
4 350622197904130331 11010519491231002X 110105491231002 110105491231996[回车]
输出样例:
Invalid[回车] Valid[回车] 11010519491231002X[回车] 110105184912319965[回车]
简要分析:
题目很长,但有用的部分不多,简而言之就是需要输入身份证号,判断位数,如果是15位则补全至18位并输出,如果是18则判断是否合法。
问题分析及解决思路:
流程分析:
0.输入身份证个数及每个身份证
1.计算身份证长度并分15位、18位两类
2.对于15位构造函数补全至18位(两位年份及一位校验码)并输出,注意年份是18还是19开头
3.对于18位构造函数判断其合法性(计算其校验码是否与所给一致)
4.本题最重要的点是考察计算和检验校验码
5.注意字符型数字和整型数字间的关系
细节方案:
0.定义二维字符数组储存,每行长度取到19以保证最后的结束标识符(\0)存在
1.使用数组储存权重、校验码并与其下标建立映射关系辅助计算及检验
2.使用循环,逐个处理身份证号
bug的出现及解决:
一顿分析猛如虎,一看结果,红了一半。这说明思路还有漏洞,究竟在哪里呢?
小插曲:我去闲鱼花了15找bug,大哥没找到bug又给我把钱退回来了hhh
后来我发现,应该是缺少了对输入的身份证位数合法性的判断,即如果输入的值不是15位或18位,则需要输出Invalid。ps:酒吧点炒饭是吧,有意思吗(
完整代码:
#include<stdio.h>
#include<string.h>
//定义数组记录权重和校验码
int w[17] = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3 ,7 ,9, 10, 5, 8, 4, 2 };
char y[11] = { '1','0','X','9','8','7','6','5','4','3','2' };
//构造函数判断18位身份证号是否合法
void solve18(char* in)
{
//定义所需变量
int i = 0, sum = 0;
//计算十七位数字本体码加权求和
for (i = 0; i < 17; i++)
{
sum += w[i] * (in[i] - 48);
}
//计算模
int Y = sum % 11;
//通过模得到对应的校验码并判断其合法性
if (y[Y] == in[17])//校验码合法
printf("Valid\n");
else//校验码不合法
printf("Invalid\n");
}
//构造函数将15位身份证号转化为18位
void solve15(char* in)
{
//定义所需变量
int i = 0;
//判断是否是19世纪出生,并补足年份位
if ((in[14] == '6' || in[14] == '7' || in[14] == '8' || in[14] == '9') && in[13] == '9' && in[12] == '9')//是19世纪出生
{
//补全出生年份“18”
for (i = 16; i >= 8; i--)
{
in[i] = in[i - 2];
}
in[6] = '1';
in[7] = '8';
}
else//非19世纪出生
{
//补全出生年份“19”
for (i = 16; i >= 8; i--)
{
in[i] = in[i - 2];
}
in[6] = '1';
in[7] = '9';
}
//计算十七位数字本体码加权求和
int sum = 0;
for (i = 0; i < 17; i++)
{
sum += w[i] * (in[i] - 48);
}
//计算模
int Y = sum % 11;
//通过模补全对应的校验码
in[17] = y[Y];
//输出18位身份证号
printf("%s\n", in);
}
//主函数部分
int main()
{
//定义变量储存输入个数
int n = 0;
scanf("%d", &n);
//定义二维字符数组储存身份证号
char in[64][19] = { '\0' };
//输入身份证号
int i = 0;
for (i = 0; i < n; i++)
scanf("%s", in[i]);
//逐个处理身份证号
for (i = 0; i < n; i++)
{
//计算身份证号长度
int l = strlen(in[i]);
//按长度分类进行传参
switch (l)
{
case 18: solve18(in[i]); break;//长度为18位
case 15: solve15(in[i]); break;//长度为15位
default: printf("Invalid\n"); break;//身份证位数不合法
}
}
return 0;
}
写在最后:
在找出bug后,我认为测试用例还是仁慈了;按照同样的想法,还可以在身份证的出生年月里埋坑,甚至埋19世纪某闰年2月的坑,这样应该还能再坑死一大批人,但我已经不想再加上这一部分了。
全文仅作为记录,欢迎大佬们的留言。