/********************************************************/
/*程序说明:身份证号码的真假识别 */
/*******************************************************/
/*身份证是否合法的判别规则*/
data sfz_test;
input id $30.;
cards;
15040419870500061X
05210419 2008000351
3302211644560000642
43504820100900033X
43564820000900033 x
435640190 10900033x
;
run;
%macro id_card_check(dataset,id_var,lib=work,id_check_result=check_ID);
data id_card;
set &dataset;
id=compress(strip(&id_var));
keep id;
run;
data check;
if _n_=1 then do;
array w w1-w18;
do i=1 to 18;
W{i}=mod(2**(i-1),11);
end;
end;
run;
option fmtsearch=(&lib);
data &lib..&id_check_result;
if _n_=1 then do;
set check;
pattern = prxparse("/[0-9]{6}(((19|20)[0-9]{2}(((0[13578]|1[02])(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)(0[1-9]|[12][0-9]|30))|(02(0[1-9]|[1][0-9]|2[0-9])))))[0-9]{3}[0-9xX]/");/*prxparse函数以正则式为输入生成一个SAS内部用于指代改正则式的id*/
end;
retain wd_sum pattern;
set ID_card;
form=prxmatch(pattern,id);
if form=0 then check_form=1;else check_form=0;/*1:检验格式是否正确*/
array w w1-w18;
format right_wd $1. ;
if check_form=0 then do;/*2:格式正确则检验身份证校验码码*/
wd_sum=0;
do i=1 to 17;
wd_sum_cal=substr(id,i,1)*w{19-i};
wd_sum=wd_sum+wd_sum_cal;
end;
wd_check=mod(wd_sum,11);
if wd_check=0 then right_wd='1';
else if wd_check=1 then right_wd='0';
else if wd_check=2 then right_wd='X';
else if wd_check=3 then right_wd='9';
else if wd_check=4 then right_wd='8';
else if wd_check=5 then right_wd='7';
else if wd_check=6 then right_wd='6';
else if wd_check=7 then right_wd='5';
else if wd_check=8 then right_wd='4';
else if wd_check=8 then right_wd='3';
else if wd_check=10 then right_wd='2';
if substr(strip(id),18,1)=right_wd then check_wd=0;else check_wd=1;/*身份证最后一位正确则check_wd=0*/
end;
if check_wd=0 then do;/*3:格式、验证码都正确的情况下检验前两位数是否属于约定的省份*/
if lead_wd>=11 or lead_wd<=15 then check_lwd=0;else/*11-15 京、津、冀、晋、蒙*/
if lead_wd>=21 or lead_wd<=23 then check_lwd=0;else/*21-23 辽、吉、黑*/
if lead_wd>=31 or lead_wd<=37 then check_lwd=0;else/*31-37 沪、苏、浙、皖、闽、赣、鲁*/
if lead_wd>=41 or lead_wd<=46 then check_lwd=0;else/*41-46 豫、鄂、湘、粤、桂、琼*/
if lead_wd>=50 or lead_wd<=54 then check_lwd=0;else/*50-54 渝、川、贵、云、藏*/
if lead_wd>=61 or lead_wd<=65 then check_lwd=0;else/*61-65 陕、甘、青、宁、新*/
if lead_wd>=81 or lead_wd<=82 then check_lwd=0;else/*81-82 港、澳*/
check_lwd=1;
end;
if max(check_form,check_wd,check_lwd)=1 then result=1;else result=0;/*1为假身份证号,0为正确身份证号码*/
drop form pattern lead_wd w1-w18 right_wd wd_sum wd_sum_cal i wd_check;
run;
quit;
proc format lib=&lib;
value result 1='假身份证号' 0='正确';
run;
proc datasets lib=&lib noprint;
modify &id_check_result;
format result result.;
label check_form='身份证格式检查'
check_wd='身份证校验码检查'
check_lwd='身份证省份码检查'
result='验证结果';
%mend id_card_check;
%id_card_check(sfz_test,id,lib=work,id_check_result=result);
proc print data=work.result;
run;