功能:检查15位或18位的身份证号码的有效性
运行环境:Red Hat Enterprise Linux AS release 3 、AIX Version 5 操作系统上测试通过
编译命令:cc -o personid_chk personid_chk.c
执行命令:./personid_chk
源代码:
/************************************************************
FileName: personid_chke.c
Author: yuanfen127
Date: 2005年01月01日
Description: 中国居民身份证检验程序
Version: 1.0
Function List:
1. int chk_pid(unsigned char *v_idno) 检查身份证校验位
2. int ATOI(char *ptr, int len) 文本类型按位转化为数值类型
3. int chkdate(char *date) 检查日期有效性
4. int get_curr_time(char *out_time) 获取当前工作日期
History:
<author> <time> <version > <desc>
***********************************************************/
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <timeb.h>
main()
{
char l_tidno[19]; /*定义19位的字符串*/
printf("请输入15位或者18位身份证件号码:"); /*提示输入身份证号*/
scanf("%18s",l_tidno); /*输入身份证号码*/
if ( 0 == chk_pid(&l_tidno) ) /*调用chk_pid函数检查身份证号*/
{
printf("此身份证号码正确!/n"); /*如果校验后正确,显示在屏幕上*/
}
return; /*返回空值*/
}
int chk_pid(unsigned char *v_idno)
{
char l_tbuf[1024]; /*存放一些文本提示信息*/
char l_tmpdate[9]; /*存放身份证中的出生日期*/
char l_check[2]; /*存放18位身份证号的校验码*/
char l_today[9]; /*存放系统当前日期*/
int l_itmp=0;
int l_inumber=0;
int i;
unsigned char l_chk_code[] = {'1','0','X','9','8','7','6','5','4','3','2'};
short l_wi[] = { 7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2,1};
/*定义省份地区数组*/
char l_tarea[92][10]={"null","null","null","null","null","null","null",
"null","null","null","null","北京","天津","河北",
"山西","内蒙古","null","null","null","null","null",
"辽宁","吉林","黑龙江","null","null","null","null",
"null","null","null","上海","江苏","浙江","安微",
"福建","江西","山东","null","null","null","河南",
"湖北","湖南","广东","广西","海南","null","null",
"null","重庆","四川","贵州","云南","西藏","null",
"null","null","null","null","null","陕西","甘肃",
"青海","宁夏","新疆","null","null","null","null",
"null","台湾","null","null","null","null","null",
"null","null","null","null","香港","澳门","null",
"null","null","null","null","null","null","null",
"国外"};
/*初始化变量*/
memset(l_tbuf,' ',sizeof(l_tbuf));
memset(l_tmpdate,' ',sizeof(l_tmpdate));
memset(l_check,' ',sizeof(l_check));
/*判断身份证号码位数*/
if ( 15 != strlen(v_idno) && 18 != strlen(v_idno) )
{
printf("身份证号码必须为15位或18位!/n");
return -1;
}
/*判断是否全部为数字*/
if ( strlen(v_idno) == 15 )
{
for ( i=0;i<15;i++ )
{
if ( 0 == isdigit( v_idno[i] ) )
{
printf("身份证输入有误!/n");
return -1;
}
}
}
else
{
/*如果是18位的身份证,前面17位均为数字*/
for ( i=0;i<17;i++ )
{
if ( 0 == isdigit(v_idno[i]) )
{
printf("身份证输入有误!/n");
return -1;
}
}
/*第18位如果不是数字,那么只可能是"x"或者"X"*/
if ( 0 == isdigit(v_idno[i]) &&
0 != memcmp(&v_idno[i],"x",1) &&
0 != memcmp(&v_idno[i],"X",1) )
{
printf("身份证输入有误!/n");
return -1;
}
}
/*身份证号码前2位代表省份地区码*/
l_itmp = ATOI(v_idno,2); /*调用ATOI函数前面2位转换成int型*/
/*组装显示信息字符串*/
sprintf(l_tbuf,"身份证号码:%s/n",v_idno);
if ( memcmp(l_tarea[l_itmp],"null",4) == 0 || l_itmp > 91 )
{
sprintf(l_tbuf,"%s地区: 不详/n",l_tbuf);
}
else
{
sprintf(l_tbuf,"%s地区: %s/n",l_tbuf,l_tarea[l_itmp]);
}
if ( strlen ( v_idno ) == 18 )
{
l_itmp = 16;
}
else
{
l_itmp = 14;
}
if ( (v_idno[l_itmp] - '0') % 2 == 0 )
{
sprintf(l_tbuf,"%s性别: 女/n",l_tbuf);
}
else
{
sprintf(l_tbuf,"%s性别: 男/n",l_tbuf);
}
printf("%s",l_tbuf);
/*判断出生日期*/
if ( strlen(v_idno) == 15 ) /*如果15身份证,默认为19XX年*/
{
sprintf(l_tmpdate,"19%.6s",&v_idno[6]);
}
else
{
sprintf(l_tmpdate,"%.8s",&v_idno[6]);
}
get_curr_time(l_today); /*调用函数获取系统当前日期*/
/*判断出生日期是否有效,并且不能晚于当前系统日期*/
if ( 0 != chkdate(&l_tmpdate) || memcmp( l_tmpdate, l_today, 8) > 0)
{
printf("身份证出生日期错误,请重新输入!/n");
return -1;
}
/*如果是18位身份证号码,则需要判断校验位*/
if ( strlen(v_idno) == 18 )
{
l_inumber=0;
/*
i 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
Wi 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2 1
S=a2*W2+a3*W3+...+ai*Wi (1<i<19)
Y=mod(S,11)
*/
/*
l_inumber=l_inumber + ATOI(&v_idno[0],1)*7;
l_inumber=l_inumber + ATOI(&v_idno[1],1)*9;
l_inumber=l_inumber + ATOI(&v_idno[2],1)*10;
l_inumber=l_inumber + ATOI(&v_idno[3],1)*5;
l_inumber=l_inumber + ATOI(&v_idno[4],1)*8;
l_inumber=l_inumber + ATOI(&v_idno[5],1)*4;
l_inumber=l_inumber + ATOI(&v_idno[6],1)*2;
l_inumber=l_inumber + ATOI(&v_idno[7],1)*1;
l_inumber=l_inumber + ATOI(&v_idno[8],1)*6;
l_inumber=l_inumber + ATOI(&v_idno[9],1)*3;
l_inumber=l_inumber + ATOI(&v_idno[10],1)*7;
l_inumber=l_inumber + ATOI(&v_idno[11],1)*9;
l_inumber=l_inumber + ATOI(&v_idno[12],1)*10;
l_inumber=l_inumber + ATOI(&v_idno[13],1)*5;
l_inumber=l_inumber + ATOI(&v_idno[14],1)*8;
l_inumber=l_inumber + ATOI(&v_idno[15],1)*4;
l_inumber=l_inumber + ATOI(&v_idno[16],1)*2;
*/
l_inumber = 0;
for ( i = 0 ; i < 17 ; i++ )
{
l_inumber += ATOI(&v_idno[i],1) * l_wi[i];
}
l_itmp = l_inumber % 11;
/*
l_itmp 0 1 2 3 4 5 6 7 8 9 10
校验码 1 0 X 9 8 7 6 5 4 3 2
*/
/*switch ( l_itmp )
{
case 0:
sprintf(l_check,"%.1s","1");
break;
case 1:
sprintf(l_check,"%.1s","0");
break;
case 2:
sprintf(l_check,"%.1s","X");
break;
default:
i = 12 - l_itmp;
sprintf(l_check,"%1d",i);
break;
}
*/
/*printf("%d/n",l_inumber);*/
/*printf("输入号码的校验位:%d/n",l_itmp);*/
/*printf("计算出正确的检验位:%d",l_check);*/
if ( v_idno[17] == 'x')
{
v_idno[17] = 'X';
}
if ( l_chk_code[l_itmp] != v_idno[17] )
{
printf("身份证校验位错误,请重新输入/n");
return -1;
}
}
return 0;
}
int ATOI(char *ptr, int len)
{
char tmpbuf[80];
int int_val;
sprintf(tmpbuf,"%.*s",len,ptr);
tmpbuf[len]='/0';
int_val=atoi(tmpbuf);
return(int_val);
}
int chkdate(char *date)
{
int day_tbl[2][13] ={ 0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
0,31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int yr,mon,day;
int leap,i;
for ( i=0; i<8; i++)
{
if( (date[i]<'0') || (date[i]>'9') )
{
return(-1);
}
}
if (sscanf(date, "%4d %2d %2d", &yr, &mon, &day) != 3)
{
return(-1);
}
if (mon < 1 || mon > 12 || day < 1 || day > 31)
{
return(-1);
}
leap = (yr % 4 == 0 && yr % 100 != 0 || yr % 400 == 0);
if (day > day_tbl[leap][mon])
{
return(-1);
}
else
{
return(0);
}
}
int get_curr_time(char *out_time)
{
struct tm *tm1;
time_t curr;
struct timeb tm2;
time(&curr);
ftime(&tm2);
tm1 = localtime (&curr);
/*返回毫秒级的当前时间*
sprintf(out_time," %04d%02d%02d %02d:%02d:%02d.%03d",
tm1->tm_year + 1900,tm1->tm_mon + 1,
tm1->tm_mday,tm1->tm_hour,
tm1->tm_min ,tm1->tm_sec,tm2.millitm);
*/
sprintf(out_time,"%04d%02d%02d",
tm1->tm_year + 1900,
tm1->tm_mon + 1,
tm1->tm_mday);
return 0;
}