正则校验之身份证

一、理论部分:

15位身份证号码=6位地区代码+6位生日+3位编号
18位身份证号码=6位地区代码+8位生日+3位编号+1位检验码

  各省市地区国家代码前两位代码是:     
  北京   11   吉林   22     福建   35   广东   44   云南   53   天津   12   黑龙江   23     江西   36   广西   45     西藏   54   河北   13     上海   31     山东   37    海南   46     陕西   61     山西   14     江苏   32     河南   41     重庆   50       甘肃   62   内蒙古   15     浙江   33     湖北   42     四川   51     青海   63       辽宁   21     安徽   34     湖南   43     贵州   52     宁夏   64     新疆   65     台湾   71     香港   81     澳门   82     国外   91   

18位身份证标准在国家质量技术监督局于1999年7月1日实施的GB11643-1999《公民身份号码》中做了明确规定。

GB11643-1999《公民身份号码》为GB11643-1989《社会保障号码》的修订版,其中指出将原标准名称“社会保障号码”更名为“公民身份号码”,另外GB11643-1999《公民身份号码》从实施之日起代替GB11643-1989。

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

1. 地址码:表示编码对象常住户口所在县(市、旗、区)的行政区划代码,按GB/T2260的规定执行。

2. 出生日期码:表示编码对象出生的年、月、日,按GB/T7408的规定执行,年、月、日分别用4位、2位、2位数字表示,之间不用分隔符。

3. 顺序码:表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。

校验的计算方式:

1. 对前17位数字本体码加权求和
公式为:S = Sum(Ai * Wi), i = 0, ... , 16
其中Ai表示第i位置上的身份证号码数字值,Wi表示第i位置上的加权因子,其各位对应的值依次为: 
              7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2

2. 以11对计算结果取模
Y = mod(S, 11)

3. 根据模的值得到对应的校验码对应关系为:
  Y值: 0 1 2 3 4 5 6 7 8 9 10
校验码: 1 0 X 9 8 7 6 5 4 3 2


说明:
  • 在ios项目的开发中可能很多地方都需要用到身份证校验,一般在开发的时候很多人都是直接百度去网上荡相关的正则表达式和校验代码,但是网上疯狂 粘贴复制的校验代码本身也可能并不准确,可能会有风险,比如2013年1月1号起停止使用15位的身份证,网上的身份证校验普遍支持15位的号码。
  • 在开发过程中,进行类似处理的时候,还是需要一些甄别的能力的,当然也要考虑自己的项目的实际情况。该文贴出了最近项目中使用到得身份证校验代码,以方便有需要的人“谨慎”获取。

二、代码示例:

对于这种需要在很多地方都用到的代码,可以使用一个工具类或者是分类来写。这里把校验的代码写在了一个工具类中。

VerifyRegexTool.h文件代码:

#import <Foundation/Foundation.h>  
  
@interface VerifyRegexTool : NSObject  
  
+ (BOOL)verifyIsNotEmpty:(NSString *)str;   //验证是否不为空  
  
+ (BOOL)verifyText:(NSString *)text withRegex:(NSString *)regex;    //正则验证  
  
+ (BOOL)verifyIDCardNumber:(NSString *)value; //验证身份证  
  
+ (BOOL)verifyCardNumberWithSoldier:(NSString *)value;   //验证军官证或警官证  
  
+ (BOOL)verifyIDCardHadAdult:(NSString *)card;  //验证身份证是否成年且小于100岁****这个方法中不做身份证校验,请确保传入的是正确身份证  
  
+ (BOOL)verifyIDCardMoreThanPointDate:(NSString *)card withNumber:(NSInteger)number withAddTimeInterval:(NSTimeInterval)interval withDateType:(DateType)dateType; //验证身份证加上指定天数是否大于指定number的类型  
  
+ (BOOL)verifyIDCardLessThanPointDate:(NSString *)card withNumber:(NSInteger)number withAddTimeInterval:(NSTimeInterval)interval withDateType:(DateType)dateType;  //验证身份证是否小于指定number的类型  
  
  
+ (NSString *)getIDCardBirthday:(NSString *)card;   //得到身份证的生日****这个方法中不做身份证校验,请确保传入的是正确身份证  
+ (NSInteger)getIDCardSex:(NSString *)card;   //得到身份证的性别(1男0女)****这个方法中不做身份证校验,请确保传入的是正确身份证  
  
@end 


VerifyRegexTool.m文件代码:
#import "VerifyRegexTool.h"

@implementation VerifyRegexTool

//验证是否不为空
+ (BOOL)verifyIsNotEmpty:(NSString *)str
{
    if (!str) return NO;
    
    str = [str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
    if (![str isEqualToString:@""]) {
        return YES;
    } else {
        return NO;
    }
}

//正则验证
+ (BOOL)verifyText:(NSString *)text withRegex:(NSString *)regex
{
    return [[text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] isMatchedByRegex:regex];
}

//验证身份证
//必须满足以下规则
//1. 长度必须是18位,前17位必须是数字,第十八位可以是数字或X
//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,71,81,82,91
//3. 第7到第14位出生年月日。第7到第10位为出生年份;11到12位表示月份,范围为01-12;13到14位为合法的日期
//4. 第17位表示性别,双数表示女,单数表示男
//5. 第18位为前17位的校验位
//算法如下:
//(1)校验和 = (n1 + n11) * 7 + (n2 + n12) * 9 + (n3 + n13) * 10 + (n4 + n14) * 5 + (n5 + n15) * 8 + (n6 + n16) * 4 + (n7 + n17) * 2 + n8 + n9 * 6 + n10 * 3,其中n数值,表示第几位的数字
//(2)余数 = 校验和 % 11
//(3)如果余数为0,校验位应为1,余数为1到10校验位应为字符串“0X98765432”(不包括分号)的第余数位的值(比如余数等于3,校验位应为9)
//6. 出生年份的前两位必须是19或20
+ (BOOL)verifyIDCardNumber:(NSString *)value
{
    value = [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    if ([value length] != 18) {
        return NO;
    }
    NSString *mmdd = @"(((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-8])))";
    NSString *leapMmdd = @"0229";
    NSString *year = @"(19|20)[0-9]{2}";
    NSString *leapYear = @"(19|20)(0[48]|[2468][048]|[13579][26])";
    NSString *yearMmdd = [NSString stringWithFormat:@"%@%@", year, mmdd];
    NSString *leapyearMmdd = [NSString stringWithFormat:@"%@%@", leapYear, leapMmdd];
    NSString *yyyyMmdd = [NSString stringWithFormat:@"((%@)|(%@)|(%@))", yearMmdd, leapyearMmdd, @"20000229"];
    NSString *area = @"(1[1-5]|2[1-3]|3[1-7]|4[1-6]|5[0-4]|6[1-5]|82|[7-9]1)[0-9]{4}";
    NSString *regex = [NSString stringWithFormat:@"%@%@%@", area, yyyyMmdd  , @"[0-9]{3}[0-9Xx]"];
    
    NSPredicate *regexTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
    if (![regexTest evaluateWithObject:value]) {
        return NO;
    }
    int summary = ([value substringWithRange:NSMakeRange(0,1)].intValue + [value substringWithRange:NSMakeRange(10,1)].intValue) *7
    + ([value substringWithRange:NSMakeRange(1,1)].intValue + [value substringWithRange:NSMakeRange(11,1)].intValue) *9
    + ([value substringWithRange:NSMakeRange(2,1)].intValue + [value substringWithRange:NSMakeRange(12,1)].intValue) *10
    + ([value substringWithRange:NSMakeRange(3,1)].intValue + [value substringWithRange:NSMakeRange(13,1)].intValue) *5
    + ([value substringWithRange:NSMakeRange(4,1)].intValue + [value substringWithRange:NSMakeRange(14,1)].intValue) *8
    + ([value substringWithRange:NSMakeRange(5,1)].intValue + [value substringWithRange:NSMakeRange(15,1)].intValue) *4
    + ([value substringWithRange:NSMakeRange(6,1)].intValue + [value substringWithRange:NSMakeRange(16,1)].intValue) *2
    + [value substringWithRange:NSMakeRange(7,1)].intValue *1 + [value substringWithRange:NSMakeRange(8,1)].intValue *6
    + [value substringWithRange:NSMakeRange(9,1)].intValue *3;
    NSInteger remainder = summary % 11;
    NSString *checkBit = @"";
    NSString *checkString = @"10X98765432";
    checkBit = [checkString substringWithRange:NSMakeRange(remainder,1)];// 判断校验位
    return [checkBit isEqualToString:[[value substringWithRange:NSMakeRange(17,1)] uppercaseString]];
}

//验证军官证或警官证
//必须是下面两种格式中的一种
//格式一:4到20位数字
//格式二:大于或等于10位并且小于等于20位(中文按两位),并满足以下规则
//1)必须有“字第”两字
//2)“字第”前面有至少一个字符
//3)“字第”后是4位以上数字
+ (BOOL)verifyCardNumberWithSoldier:(NSString *)value
{
    NSString *s1 = @"^\\d*$";
    NSString *s2 = @"^.{1,}字第\\d{4,}$";
    //NSString *s3 = @"^([A-Za-z0-9\\u4e00-\\u9fa5])*$";
    value = [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    if ([VerifyRegexTool verifyText:value withRegex:s1]) {
        NSString *s11 = @"^\\d{4,20}$";
        return [value isMatchedByRegex:s11];
    } else if ([self lengthUsingChineseCharacterCountByTwo:value] >= 10
               && [self lengthUsingChineseCharacterCountByTwo:value] <= 20) {
        return [value isMatchedByRegex:s2];
    }
    
    return NO;
}

+ (NSUInteger)lengthUsingChineseCharacterCountByTwo:(NSString *)string{
    NSUInteger count = 0;
    for (NSUInteger i = 0; i< string.length; ++i) {
        if ([string characterAtIndex:i] < 256) {
            count++;
        } else {
            count += 2;
        }
    }
    return count;
}

//验证身份证是否成年且小于100岁****这个方法中不做身份证校验,请确保传入的是正确身份证
+ (BOOL)verifyIDCardHadAdult:(NSString *)card
{
    NSString *birtday = [VerifyRegexTool getIDCardBirthday:card];   //****年**月**日
    //转换为****-**-**
    birtday = [birtday stringByReplacingOccurrencesOfString:@"年" withString:@"-"];
    birtday = [birtday stringByReplacingOccurrencesOfString:@"月" withString:@"-"];
    birtday = [birtday stringByReplacingOccurrencesOfString:@"日" withString:@""];
    birtday = [NSString stringWithFormat:@"%@ 00:00",birtday];
    NSDate *dateBirtday = [NSDate dateFromString:birtday];
    NSInteger year = [[NSDate date] getIntervalTime:dateBirtday withDateType:DateTypeYear];   //得到年数
    
    if (year >= 18 && year < 100) {
        return YES;
    } else {
        return NO;
    }
    
}

//验证身份证加上指定天数是否大于指定number的类型
+ (BOOL)verifyIDCardMoreThanPointDate:(NSString *)card withNumber:(NSInteger)number withAddTimeInterval:(NSTimeInterval)interval withDateType:(DateType)dateType {
    NSString *birtday = [VerifyRegexTool getIDCardBirthday:card];   //****年**月**日
    //转换为****-**-**
    birtday = [birtday stringByReplacingOccurrencesOfString:@"年" withString:@"-"];
    birtday = [birtday stringByReplacingOccurrencesOfString:@"月" withString:@"-"];
    birtday = [birtday stringByReplacingOccurrencesOfString:@"日" withString:@""];
    birtday = [NSString stringWithFormat:@"%@ 00:00",birtday];
    NSDate *dateBirtday = [NSDate dateFromString:birtday];
    NSDate *today = [NSDate dateFromStringWithoutTime:[[NSDate date] strDate]];
    NSDate *pointDate = [today dateByAddingTimeInterval:interval];
    
    //tempDate为number后的日期
    NSDate *tempDate = [pointDate dateAddNumber:-number withDateType:dateType];  //临界日期
    if ([dateBirtday earlierDate:tempDate] == dateBirtday) {  //如果生日+指定天数大雨tempdate,则通过
        return YES;
    } else {
        return NO;
    }
}

//验证身份证加上指定天数是否小于指定number的类型
+ (BOOL)verifyIDCardLessThanPointDate:(NSString *)card withNumber:(NSInteger)number withAddTimeInterval:(NSTimeInterval)interval withDateType:(DateType)dateType
{
    NSString *birtday = [VerifyRegexTool getIDCardBirthday:card];   //****年**月**日
    //转换为****-**-**
    birtday = [birtday stringByReplacingOccurrencesOfString:@"年" withString:@"-"];
    birtday = [birtday stringByReplacingOccurrencesOfString:@"月" withString:@"-"];
    birtday = [birtday stringByReplacingOccurrencesOfString:@"日" withString:@""];
    birtday = [NSString stringWithFormat:@"%@ 00:00",birtday];
    NSDate *dateBirtday = [NSDate dateFromString:birtday];
    NSDate *today = [NSDate dateFromStringWithoutTime:[[NSDate date] strDate]];
    NSDate *pointDate = [today dateByAddingTimeInterval:interval];
    
    //tempDate为number后的日期
    NSDate *tempDate = [pointDate dateAddNumber:-number withDateType:dateType];  //临界日期
    if ([tempDate earlierDate:dateBirtday] == tempDate) {  //如果生日+指定天数小于tempdate,则通过
        return YES;
    } else {
        return NO;
    }
}

//得到身份证的生日****这个方法中不做身份证校验,请确保传入的是正确身份证
+ (NSString *)getIDCardBirthday:(NSString *)card {
    card = [card stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    if ([card length] != 18) {
        return nil;
    }
    NSString *birthady = [NSString stringWithFormat:@"%@年%@月%@日",[card substringWithRange:NSMakeRange(6,4)], [card substringWithRange:NSMakeRange(10,2)], [card substringWithRange:NSMakeRange(12,2)]];
    return birthady;
}

//得到身份证的性别(1男0女)****这个方法中不做身份证校验,请确保传入的是正确身份证
+ (NSInteger)getIDCardSex:(NSString *)card {
    card = [card stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    NSInteger defaultValue = 0;
    if ([card length] != 18) {
        return defaultValue;
    }
    NSInteger number = [[card substringWithRange:NSMakeRange(16,1)] integerValue];
    if (number % 2 == 0) {  //偶数为女
        return 0;
    } else {
        return 1;
    }
}

@end



身份证校验调用示例:

if (![VerifyRegexTool verifyIDCardNumber:self.IdentityCardNOTextfield.text]) {   //验证身份证是否真实  
          [self showMessageHUD:@"请输入正确的身份证号码!" withTimeInterval:kShowMessageTime];  
          return;  
} 


转载请注明出处http://blog.csdn.net/chen_gp_x


发布了110 篇原创文章 · 获赞 124 · 访问量 100万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 撸撸猫 设计师: 设计师小姐姐

分享到微信朋友圈

×

扫一扫,手机浏览