Swift 常用正则

解决问题

1、身份证校验
2、三字码校验
3、手机号校验
4、票号校验
5、邮箱校验
6、url校验
7、全英文校验
8、全数字校验
9、全中文校验

代码

/// 身份证校验错误类型
public enum IDErrorType {
    /// 位数错误(身份证需18位)
    case DigitError(String)
    /// 1-2位省份编码有误
    case ProvinceError(String)
    /// 3-4位城市编码有误
    case CityError(String)
    /// 4-5位区县编码有误
    case DistrictError(String)
    /// 7-14位出生日期编码有误
    case BirthDayError(String)
    /// 15-17位同地区出生顺序码有误
    case OrderError(String)
    /// 第18位校验码与前17位本体码不匹配
    case CheckCodeMismatch(String)
}
/// 字符串别名 RegexPattern
public typealias RegexPattern = String

private extension RegexPattern {
    /// 三字码 正则
    static let CODE_REGEX = "^[a-zA-Z]{3}$"
    /// 手机号 正则
    static let PHONE_NUMBER_REGEX = "^1(3|4|5|6|7|8|9)\\d{9}$"
    /// 电子客票 正则
    static let E_TICKET_NUMBER_REGEX = "^[0-9]{13}$"
    /// url地址  正则
    static let URL_REGEX = "^http(s)?:\\/\\/([\\w-]+\\.)+[\\w-]+(\\/[\\w- .\\/?%&=]*)?$"
    /// 邮箱 正则
    static let EMAIL_REGEX = "^[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}$"
    /// 中文 正则
    static let ZH_CN_REGEX = "^[\\u4e00-\\u9fa5]+$"
    /// 英文 正则
    static let EN_REGEX = "^[A-Za-z]+$"
    /// 数字 正则
    static let NUM_REGEX = "^[0-9]+$"
}

/// 正则校验工具类
public class RegexUtil {
    
    /// 校验字符串是否为三字码
    /// 规则:由3个英文(大/小写)字母组成
    /// - Parameter isNormalCode: 输入内容
    /// - Returns: true/false
    public class func isNormalCode(_ cityCode: String) -> Bool {
        return evaluate(with: .CODE_REGEX, string: cityCode)
    }
    
    /// 校验字符串是否为手机号码
    /// 规则:11位数字,以1开头,第二位为3|4|5|6|7|8|9
    /// - Parameter phoneNumber: 输入内容
    /// - Returns: true/false
    public class func isPhoneNumber(_ phoneNumber: String) -> Bool {
        return evaluate(with: .PHONE_NUMBER_REGEX, string: phoneNumber)
    }
    
    /// 校验字符串是否为电子客票(E-ticket)号
    /// 规则:13位数字
    /// - Parameter ticketNumber: 输入内容
    /// - Returns: true/false
    public class func isETicketNumber(_ ticketNumber: String) -> Bool {
        return evaluate(with: .E_TICKET_NUMBER_REGEX, string: ticketNumber)
    }
    
    /// 校验字符串是否为url
    /// 规则:略
    /// - Parameter url: 输入内容
    /// - Returns: true/false
    public class func isUrl(_ url: String) -> Bool {
        return evaluate(with: .URL_REGEX, string: url)
    }
    
    /// 校验字符串是否为邮箱
    /// 规则:略
    /// - Parameter email: 输入内容
    /// - Returns: true/false
    public class func isEmail(_ email: String) -> Bool {
        return evaluate(with: .EMAIL_REGEX, string: email)
    }
    
    /// 校验字符串是否全是中文
    /// 规则:全中文简体
    /// - Parameter string: 输入内容
    /// - Returns: true/false
    public class func isAllChinese(_ string: String) -> Bool {
        return evaluate(with: .ZH_CN_REGEX, string: string)
    }
    
    /// 校验字符串是否全是英文
    /// 规则:全英文字符串,不区分大小写
    /// - Parameter string: 输入内容
    /// - Returns: true/false
    public class func isAllEnglish(_ string: String) -> Bool {
        return evaluate(with: .EN_REGEX, string: string)
    }
    
    /// 校验字符串是否全是数字
    /// 规则:纯数字字符串
    /// - Parameter string: 输入内容
    /// - Returns: true/false
    public class func isAllNumber(_ string: String) -> Bool {
        return evaluate(with: .NUM_REGEX, string: string)
    }
    
    /// 通用正则验证
    /// - Parameter regex: 正则表达式
    /// - Parameter string:被验证的字符串
    /// - Returns: true/false
    public static func evaluate(with regex: RegexPattern, string: String) -> Bool {
        let predic = NSPredicate(format: "SELF MATCHES %@", argumentArray: [regex])
        return predic.evaluate(with: string)
    }

	/// 检查身份证号的正确性
    /// - Parameter idCard: 身份证号
    /// - Returns: 校验结果错误类型(nil为无错误)
    public class func checkIdentify(_ idCard: String) -> IDErrorType? {
        // 1.位数为18位
        guard idCard.count == 18 else {
            return IDErrorType.DigitError("身份证需18位")
        }
        
        // 2.前两位省份编码
        let provinceMap: [String: String] = ["北京": "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"]
        guard let provinceCode = try? idCard.subString(to: 2) else {
            return IDErrorType.ProvinceError("第1-2位省份编码有误")
        }
        let provinceCodeList = provinceMap.map {
            $0.value
        }
        if !provinceCodeList.contains(provinceCode) {
            return IDErrorType.ProvinceError("第1-2位省份编码有误")
        }
        
        // 3.第3-4位为城市(0-9数字)
        guard let regionCode = try? idCard.subString(from: 2, to: 4) else {
            return IDErrorType.CityError("第3-4位城市编码有误")
        }
        guard evaluate(with: .NUM_REGEX, string: regionCode) else {
            return IDErrorType.CityError("第3-4位城市编码有误")
        }
        
        // 3.第5-6位为区县编码(0-9数字)
        guard let regionCode = try? idCard.subString(from: 4, to: 6) else {
            return IDErrorType.DistrictError("第5-6位区县编码有误")
        }
        guard evaluate(with: .NUM_REGEX, string: regionCode) else {
            return IDErrorType.DistrictError("第5-6位区县编码有误")
        }
        
        // 4.第7-14位为生日日期
        guard let birthday = try? idCard.subString(from: 6, to: 14) else {
            return IDErrorType.BirthDayError("第7-14位生日编码有误")
        }
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyyMMdd"
        guard let birthDayDate = dateFormatter.date(from: birthday) else {
            return IDErrorType.BirthDayError("第7-14位生日编码有误")
        }
        // 超过当前日期
        let currentDate = Date()
        let daysDif = Date.daysBetween(from: birthDayDate, to: currentDate)
        if daysDif < 0 {
            return IDErrorType.BirthDayError("第7-14位生日编码有误")
        }
        // 距离当前日期超过150年
        let yearDif = currentDate.year() - birthDayDate.year()
        if yearDif > 150 {
            return IDErrorType.BirthDayError("第7-14位生日编码有误")
        }
        
        // 5.第15-17位都是同一地址辖区内的,以及同年同月同日出生人的顺序码, 同时第17位兼具性别标识功能,奇数分配给男,偶数分配给女(0-9数字)
        guard let orderCode = try? idCard.subString(from: 14, to: 17) else {
            return IDErrorType.OrderError("第15-17位同地区出生顺序码有误")
        }
        guard evaluate(with: .NUM_REGEX, string: orderCode) else {
            return IDErrorType.OrderError("第15-17位同地区出生顺序码有误")
        }
        
        // 6.第18位为前17位本体码的校验码(0-9或x)
        guard let checkCode = try? idCard.subString(from: 17) else {
            return IDErrorType.CheckCodeMismatch("第18位校验码有误")
        }
        let checkCodePattern: RegexPattern = "[0-9xX]"
        guard evaluate(with: checkCodePattern, string: checkCode) else {
            return IDErrorType.CheckCodeMismatch("第18位校验码只能是0-9的数字或字母x")
        }
        // 再根据前17位校验第18位校验码是否正确
        // 将前17位加权因子保存在数组
        let idCardWi = ["7", "9", "10", "5", "8", "4", "2", "1", "6", "3", "7", "9", "10", "5", "8", "4", "2"]
        // 这是除以11后,可能产生的11位余数(10就是x),验证码,也保存成数组
        let idCardY = ["1", "0", "10", "9", "8", "7", "6", "5", "4", "3", "2"]
        // 用来保存前17位各自乘以加权因子后的总和
        var idCardWiSum = 0
        for index in 0..<17 {
            if let numStr = try? idCard.subString(from: index, to: index+1), let numInt = Int(numStr), let numWiInt = Int(idCardWi[index]) {
                idCardWiSum += numInt * numWiInt
            }
        }
        
        // 计算出校验码所在数组的位置
        let idCardMod = idCardWiSum % 11
        // 如果等于2,则说明校验码是10,身份证号最后一位应该是x
        if idCardMod == 2 {
            return checkCode.lowercased() == "x" ? nil : IDErrorType.CheckCodeMismatch("第18位校验码与前17位本体码不匹配")
        } else {
            // 用计算出的校验码与最后一位身份证号码匹配
            if let checkCodeInt = Int(checkCode), let idCardYInt = Int(idCardY[idCardMod]), checkCodeInt == idCardYInt {
                return nil
            } else {
                return IDErrorType.CheckCodeMismatch("第18位校验码与前17位本体码不匹配")
            }
        }
    }
    
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张子都

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值