问题描述

测试代码

解决思路

代码实现

身份证号码的校验

日期的校验

总结

用C语言实现身份证号码校验码和日期校验

小葵

2024-06-22

🏷

面试题

本文发表于入职啦(公众号: ruzhila) 大家可以访问入职啦学习更多的编程实战。

问题描述

身份证号码的验证是一个常见的需求,身份证的号码由17位数字和1位校验码组成,身份证的格式由:

  • 6位地址码
  • 8位出生日期码 格式为YYYYMMDD
  • 3位顺序码组成
  • 最后一位是校验码

校验码的计算方法如下:

  1. 将前17位数字的权值分别乘以系数相加
  2. 将相加的结果除以11,得到余数
  3. 通过余数查找校验码的值
  4. 如果校验码的值等于身份证号码的最后一位,则校验通过
  5. 如果校验码的值等于10,则校验码为X
  6. 如果校验码的值不等于身份证号码的最后一位,则校验不通过
  7. 日期的校验,年份的范围是1900-2099年
  8. 日期的校验,月份的范围是1-12月
  9. 日期的校验,日期的范围是1-31日
  10. 日期的校验,闰年的2月份是29天
  11. 日期的校验,平年的2月份是28天
  12. 日期的校验,4、6、9、11月份是30天
  13. 日期的校验,1、3、5、7、8、10、12月份是31天
  14. 日期的校验,2月份是28天
  15. 日期的校验,闰年的2月份是29天
  16. 日期的校验,闰年的判断方法是年份能被4整除,但是不能被100整除,或者能被400整除

测试代码

    assertEqual(id_verify("11010519491231002X"), 'X', "1");
    assertEqual(id_verify("110105194912310021"), '\0', "2");
    assertEqual(id_verify("350703198001001237"), '7', "2");

解决思路

根据 GB11643-1999中有关公民身份号码的规定,公民身份号码是特征组合码,由十七位数字本体码和一位数字校验码组成。 校验码的计算方法如下:

  1. 将前17位数字的权值分别乘以系数相加: {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}
  2. 将相加的结果除以11,得到余数
  3. 通过余数查找校验码的值: {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'}
  4. 对日期进行校验,年份的范围是1900-2099年,月份的范围是1-12月,日期的范围是1-31日,闰年的2月份是29天,平年的2月份是28天,4、6、9、11月份是30天,1、3、5、7、8、10、12月份是31天

代码实现

身份证号码的校验

如果身份证号码的校验通过,则返回校验码,否则返回'\0',表示校验不通过

char calc_checksum(const char *card) {
    // 加权因子
    int weight[] = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
    // 校验码对应值
    char checkCodes[] = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};
    int sum = 0;
    if (card == NULL) {
        return '\0';
    }
    for (int i = 0; i < 17; i++) {
        int c = card[i];
        if (c < '0' || c > '9') {
            return '\0';
        }
        sum += (c - '0') * weight[i];
    }
    return checkCodes[sum % 11];
}

日期的校验

int isvalid_date(const char *card) {
    const char *date = card + 6;
    int year = 0;
    for (int i = 0; i < 4; i++) {
        year = year * 10 + (date[i] - '0');
    }
    if (year < 1900 || year > 2099) {
        return 0;
    }
    
    int month = 0;
    for (int i = 4; i < 6; i++) {
        month = month * 10 + (date[i] - '0');
    }
    if (month < 1 || month > 12) {
        return 0;
    }
    
    int day = 0;
    for (int i = 6; i < 8; i++) {
        day = day * 10 + (date[i] - '0');
    }

    if (day < 1 || day > 31) {
        return 0;
    }
    
    if (month == 2) {
        if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
            if (day > 29) {
                return 0;
            }
        } else {
            if (day > 28) {
                return 0;
            }
        }
    } else if (month == 4 || month == 6 || month == 9 || month == 11) {
        if (day > 30) {
            return 0;
        }
    }
    return 1;
}

完整代码请参考: idverify.c

总结

日期和校验码分别实现了校验的逻辑,这样当验证码正确的情况下我们再做日期的校验,可以减少不必要的计算。 整个代码没有使用任何的库,只是使用了C语言的基本语法,通过这个练习,我们可以学习如何使用C语言实现一个简单的校验算法。 日期的解析是比较简单的,主要是对年份、月份和日期的校验,通过这个练习,我们可以学习如何解析日期。

所有的后端面试常见的问题,我们每天都会在我们的编程群里面讨论和Code review, 欢迎大家加入我们的编程群,一起学习和进步。

编程交流群

欢迎大家关注 入职啦 (公众号: ruzhila) ,获取更多有趣的编程挑战题和技术干货!

友情链接:

Copyright© 2024 杭州园中葵科技有限公司 版权所有