身份证的秘密(高级)——PTA

/*特别鸣谢——猫猫学姐*/

小明特别想知道自己身份证号码的秘密,那我就告诉你吧!

公民身份号码是特征组合码,由十七位数字本体码和一位校验码组成。排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位校验码,可以用字母表示为:

ABCDEFYYYYMMDDXXXR。

1.地址码ABCDEF。表示编码对象常住户口所在县(市、旗、区)的行政区划代码,按GB/T2260标准的规定执行。
2.出生日期码YYYYMMDD。表示编码对象出生的年、月、日,按GB/T7408标准的规定执行,年、月、日代码之间不用分隔符。
3.顺序码XXX。表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。
4.校验码R。根据前面十七位数字码,按照ISO 7064:1983.MOD 11-2标准校验码计算出来的检验码。

18位身份证号码的编排规则: 
(1)前1、2位数字表示:所在省份的代码; 
11:"北京",12:"天津",13:"河北",14:"山西",15:"内蒙古"
21:"辽宁",22:"吉林",23:"黑龙江"
31:"上海",32:"江苏",33:"浙江",34:"安徽",35:"福建",36:"江西",37:"山东"
41:"河南",42:"湖北",43:"湖南",44:"广东",45:"广西",46:"海南"
50:"重庆",51:"四川",52:"贵州",53:"云南",54:"西藏"
61:"陕西",62:"甘肃",63:"青海",64:"宁夏",65:"新疆"
81:"香港",82:"澳门",83:"台湾地区
91:"国外"
(2)第3、4位数字表示:所在城市的代码; 
(3)第5、6位数字表示:所在区县的代码; 
(4)第7~14位数字表示:出生年、月、日; 
(5)第15、16位数字表示:所在地的派出所的代码; 
(6)第17位数字表示性别:奇数表示男性,偶数表示女性; 
(7)第18位数字是校检码:是由计算产生的,用来检验身份证的正确性。校检码可以是0~9的数字,有时也用x表示。

第十八位数字的计算方法为: 
(1)将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:
7、9、10、5、8、4、2、1、6、3、7、9、10、5、8、4、2
(2)将这17位数字和系数相乘的结果相加。
(3)用加出来和除以11,看余数是多少?
(4)余数只可能有0  1  2   3  4  5  6  7  8  9 10 这11个数字。其分别对应的最后一位
身份证的号码为1  0  X   9  8  7  6  5  4  3  2。
(5)通过上面得知如果余数是2,就会在身份证的第18位数字上出现罗马数字的Ⅹ。如果余数是10,身份证的最后一位号码就是2。 

例如:某男性的身份证号码是34052419800101001X。我们要看看这个身份证是不是合法的身份证。 
首先:我们得出,前17位数字与相应系数的乘积和是189。
然后:用189除以11得出的结果是17余2,也就是说余数是2。
最后:通过对应规则就可以知道余数2对应的数字是x。所以,这是一个合格的身份证号码。

现在,于老师给你一个身份证号,你能知道它是不是一个合法的号码?如果是合法的你知道这个号码是男是女吗?

输入格式:

一行中一串号码,最多18位,可能不是18位,每位也可能不是数字或X,没空格。

输出格式:
请按如下顺序判断并输出结果
1.不够18位,直接输出(X为实际读到的位数):
No WeiShuBuZu:X
以下情况需要首先一行原样输出身份证号,如果其中有非法字符用括号括上。
2.有非法字符,即前17位不是数字或第18位不是数字和X,先输出号码本身(非法字符用括号括上),再输出(X为非法字符个数):
No FeiFaZiFu:X
3.省份代码不合法,输出(XX为出错的省份码):
No ShengFenDaiMa:XX
4.出生日期不是合法日期,输出(YYYYMMDD为出错的日期):
No ChuShengRiQi:YYYYMMDD
5.第18位校验代码错误,输出(E为错误代码R为计算得到的正确代码):
No JiaoYanHaoMa:E(R is right)
(不合法的号码只需要按以上顺序输出第一个原因)。
合法号码根据性别输出:Yes male(男)或者Yes female(女)
输入样例1:
34052419800101001
输出样例1:
No WeiShuBuZu:17
输入样例2:
3$0524198:010100Rx
输出样例2:
3($)0524198(:)010100(R)x
No FeiFaZiFu:3
输入样例3:
25042120021715082x
输出样例3:
25042120021715082x
No ShengFenDaiMa:25
输入样例4:
23042120021715082x
输出样例4:
23042120021715082x
No ChuShengRiQi:20021715
输入样例5:
340524198001010017
输出样例5:
340524198001010017
No JiaoYanHaoMa:7(X is right)
输入样例6:
34052419800101001X
输出样例6:
34052419800101001X
Yes male
#include <stdio.h>
#include <string.h>

int count = 0;
char jym;
char s[19];

int ch(char s[]) //字符,前17位不是数字或18位不是数字/x
{
    char b[19] = {0}; //b为哈希表,记录非法位置
    for (int i = 0; i < 18; i++) //判断并记录是否合法
    {
        if (s[i] < '0' || s[i] > '9')
        {
            if (i == 17 && (s[i] == 'x' || s[i] == 'X'))
                continue;
            b[i] = 1;
            count++;
        }
    }
    if (count != 0)
    {
        for (int i = 0; i < 18; i++)
        {
            if (b[i] == 1)
            {
                printf("(%c)", s[i]);
            }
            else
                printf("%c", s[i]);
        }
        printf("\n");
        return 1;
    }
    return 0;
}

int province(char s[]) //省份
{
    int pro[10][10] = {{0}, {0, 1, 1, 1, 1, 1}, {0, 1, 1, 1}, {0, 1, 1, 1, 1, 1, 1, 1}, {0, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, {0, 1, 1, 1, 1, 1}, {0}, {0, 1, 1, 1}, {0, 1}};
    //二维数组哈希表储存合法省份代码,第一位为行第二位为列,合法置1
    if (pro[(int)(s[0] - '0')][(int)(s[1] - '0')] != 1)
        return 1;
    return 0;
}

int birth(char s[]) //生日
{
    int mon[15] = {0, 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //数组储存每个月日期,二月单独算
    int y, m, d;
    y = 1000 * (s[6] - '0') + 100 * (s[7] - '0') + 10 * (s[8] - '0') + (s[9] - '0');
    m = 10 * (s[10] - '0') + (s[11] - '0');
    d = 10 * (s[12] - '0') + (s[13] - '0');
    if (m < 1 || m > 12)
        return 1;
    if (m == 2)
    {
        if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0)
            mon[2] = 29;
        else
            mon[2] = 28;
    }
    if (d < 1 || d > mon[m])
        return 1;
    return 0;
}

int ISO(char s[]) //校验码
{
    int xi[20] = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
    char yu[15] = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};
    //哈希表预处理系数和余数
    int sum = 0, remainder;
    for (int i = 0; i < 17; i++)
        sum += xi[i] * (s[i] - '0');
    remainder = sum % 11;
    jym = yu[remainder];
    if (s[17] != jym)
    {
        if (jym == 'X' && s[17] == 'x')
            return 0;
        return 1;
    }
    return 0;
}

int main()
{
    scanf("%s", s);
    if (strlen(s) < 18)
    {
        printf("No WeiShuBuZu:%lu\n", strlen(s));
    }
    else if (ch(s) == 1)
    {
        printf("No FeiFaZiFu:%d\n", count);
    }
    else if (province(s) == 1)
    {

        printf("No ShengFenDaiMa:%c%c\n", s[0], s[1]);
    }
    else if (birth(s) == 1)
    {

        printf("No ChuShengRiQi:");
        for (int i = 6; i < 14; i++)
            printf("%c", s[i]);
        printf("\n");
    }
    else if (ISO(s) == 1)
    {

        printf("No JiaoYanHaoMa:%c(%c is right)\n", s[17], jym);
    }
    else
    {

        if ((s[16] - '0') % 2 == 0)
            printf("Yes female\n");
        else
            printf("Yes male\n");
    }
    return 0;
}

  • 35
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值