一个合法的身份证号码由17位地区、日期编号和顺序编号加1位校验码组成。校验码的计算规则如下:
首先对前17位数字加权求和,权重分配为:{7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};然后将计算的和对11取模得到值Z;最后按照以下关系对应Z值与校验码M的值:
Z:0 1 2 3 4 5 6 7 8 9 10
M:1 0 X 9 8 7 6 5 4 3 2
现在给定一些身份证号码,请你验证校验码的有效性,并输出有问题的号码。
输入格式:
输入第一行给出正整数N(≤100)是输入的身份证号码的个数。随后N行,每行给出1个18位身份证号码。
输出格式:
按照输入的顺序每行输出1个有问题的身份证号码。这里并不检验前17位是否合理,只检查前17位是否全为数字且最后1位校验码计算准确。如果所有号码都正常,则输出All passed。
输入样例1:
4
320124198808240056
12010X198901011234
110108196711301866
37070419881216001X
输出样例1:
12010X198901011234
110108196711301866
37070419881216001X
输入样例2:
2
320124198808240056
110108196711301862
输出样例2:
All passed
这是我本来写了很久才得到的
#include<stdio.h>//目前只差判断前17位都是数字
int main()
{
int N,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,j1=0;
char r;
scanf("%d",&N);
for(int j2=1;j2<=N;j2++)
{
scanf("%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d%1d%c",&a,&b,&c,&d,&e,&f,&g,&h,&i,&j,&k,&l,&m,&n,&o,&p,&q,&r);//读入各位数字
int sum,z;
sum=(a*7+b*9+c*10+d*5+e*8+f*4+g*2+h*1+i*6+j*3+k*7+l*9+m*10+n*5+o*8+p*4+q*2);
z=sum%11;
char r1;
switch(z)
{
case 0:r1='1';break;
case 1:r1='0';break;
case 2:r1='X';break;
case 3:r1='9';break;
case 4:r1='8';break;
case 5:r1='7';break;
case 6:r1='6';break;
case 7:r1='5';break;
case 8:r1='4';break;
case 9:r1='3';break;
case 10:r1='2';break;
}
if(r1!=r)
{
printf("%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%c\n",a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r);
}
else
{
j1++;
}
}
if(j1==N)
{
printf("All passed");
}
return 0;
}
后来发现没有办法判断前17位输入的是数字,所以这个方案不好。
并且,写得很乱,改都不好改
以上写于三月十几
···························································································
20200322更新
今天重新写一遍,换了一部分思路,用了新知识
经过很长时间(一个半小时),我终于过了。这次收获的更多了。
代码如下
#include<stdio.h>
int main()
{
int n,sta=0,sum=0,z,per=0;//sta记录状态,用来判断前17位是否都是数字;sum为加权求和得到的值;z是对11取模后所得;per是通过的身份证号码数
char r1;//r1是正确校验值
scanf("%d\n",&n);//n组数据
char a[18];
int c[17]={7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};
for(int i=1;i<=n;i++)
{
//输入18位身份证号码
for(int j=0;j<=18;j++)
{
a[j]=getchar();
}
//接下来判断前17位是否都是数字
for(int o=0;o<=16;o++)
{
if((a[o]>=48)&&(a[o]<=57))continue;//第o+1位是数字则继续此次循环
sta=1;//有某一位不是数字则sta=1
}
if(sta==1)//sta=1说明有某一位不是数字
{
for(int v=0;v<=17;v++)
{
printf("%c",a[v]);
}
printf("\n");
sta=0;//复原sta的值
continue;
}
//就直接把此身份证号输出并结束此次循环
//前17位都是数字则继续
//首先加权求和
for(int q=0;q<=16;q++)
{
sum=sum+(a[q]%48)*c[q];
}
//再与11取模
z=sum%11;
//再一一对应
switch(z)
{
case 0:r1='1';break;
case 1:r1='0';break;
case 2:r1='X';break;
case 3:r1='9';break;
case 4:r1='8';break;
case 5:r1='7';break;
case 6:r1='6';break;
case 7:r1='5';break;
case 8:r1='4';break;
case 9:r1='3';break;
case 10:r1='2';break;
}
//再与所输校验值对比
if(r1==a[17])
{
per++;//记录通过数加一
}
else
{
for(int p=0;p<=17;p++)
{
printf("%c",a[p]);
}
printf("\n");
}
sum=0;
z=0;//复原sum,z的值
}
if(per==n)
{
printf("All passed");
}
return 0;
}
这次我很注意写注释,就不会像上次一样混乱。
通过做本题也学到不少
- 在DEVC++中调试时要想一次性将一个数组添加查看可以这样:
可以通过调试的时候添加查看:比如说有一个长度为3的vector v,如果想要查看v[0]的值,就在添加查看中写 * (&v[0])
如果想要查看整个数组的值,就可以写*(&v[0])@3
@后面的数字表示想要查看的长度,这里vector的长度是3所以可以写3就能看到所有的值~1
- 思路上,将身份证号以字符格式输入才有利于判断前17位是否为数字(由ASCII值判断)
- 合理利用数组可以方便许多(比如你可以对比一下我的之前的代码,17个乃至18个%d输得烦人,明显是笨功夫)——我以后见到数组不再头大了。
也遇到许多问题
- 第六行之所以有一个\n是因为,原本没有时getchar()会将换行符输入进a[0],造成错误,这个可以加\n解决,也可以不用getchar(),换成scanf
- 低级错误,最开始没有在合适的时机将sta, z, sum的值恢复一下,以至于他们的值累积下来造成第一个结果正确,后续全部错误的结果
- 第31行处的continue位置最开始错误,错放到了 } 之后,就造成了错误结束本次循环
- 格式问题,每个错误身份证号输出后要换行——\n
还要加强的部分
- 主要是得再勤奋点,多学些东西,比如这次刚学到continue就用上了。