解决问题
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位本体码不匹配")
}
}
}
}