六、字符串有关数字方面的扩展
// MARK:- 五、字符串有关数字方面的扩展 public enum StringCutType { case normal, auto } public extension String { // MARK: 5.1、将金额字符串转化为带逗号的金额 按照千分位划分,如 "1234567" => 1,234,567 1234567.56 => 1,234,567.56 /// 将金额字符串转化为带逗号的金额 按照千分位划分,如 "1234567" => 1,234,567 1234567.56 => 1,234,567.56 /// - Returns: 千分位的字符串 func toThousands() -> String? { let formatter = NumberFormatter() formatter.numberStyle = .decimal formatter.roundingMode = .floor formatter.maximumFractionDigits = 0 formatter.minimumFractionDigits = 0 if self.contains(".") { formatter.maximumFractionDigits = 2 formatter.minimumFractionDigits = 2 formatter.minimumIntegerDigits = 1 } var num = NSDecimalNumber(string: self) if num.doubleValue.isNaN { num = NSDecimalNumber(string: "0") } let result = formatter.string(from: num) return result } // MARK: 5.2、字符串差不多精确转换成Double——之所以差不多,是因为有精度损失 /// - Important: 字符串差不多精确转换成Double——之所以差不多,是因为有精度损失 func accuraterDouble() -> Double? { guard let decimal = Decimal(string: self) else { return nil } JKPrint(NSDecimalNumber(decimal: decimal).doubleValue) return NSDecimalNumber(decimal: decimal).doubleValue } // MARK:- 5.3、去掉小数点后多余的0 /// 去掉小数点后多余的0 /// - Returns: 返回小数点后没有 0 的金额 func cutLastZeroAfterDot() -> String { var rst = self var i = 1 if self.contains(".") { while i < self.count { if rst.hasSuffix("0") { rst.removeLast() i = i + 1 } else { break } } if rst.hasSuffix(".") { rst.removeLast() } return rst } else { return self } } // MARK: 5.4、将数字的字符串处理成 几位 位小数的情况 /// 将数字的字符串处理成 几位 位小数的情况 /// - Parameters: /// - numberDecimal: 保留几位小数 /// - mode: 模式 /// - Returns: 返回保留后的小数,如果是非字符,则根据numberDecimal 返回0 或 0.00等等 func saveNumberDecimal(numberDecimal: Int = 0, mode: NumberFormatter.RoundingMode = .floor) -> String { var n = NSDecimalNumber(string: self) if n.doubleValue.isNaN { n = NSDecimalNumber.zero } let formatter = NumberFormatter() formatter.roundingMode = mode // 小数位最多位数 formatter.maximumFractionDigits = numberDecimal // 小数位最少位数 formatter.minimumFractionDigits = numberDecimal // 整数位最少位数 formatter.minimumIntegerDigits = 1 // 整数位最多位数 formatter.maximumIntegerDigits = 100 // 获取结果 guard let result = formatter.string(from: n) else { // 异常处理 if numberDecimal == 0 { return "0" } else { var zero = "" for _ in 0..<numberDecimal { zero += zero } return "0." + zero } } return result } }
七、苹果针对浮点类型计算精度问题提供出来的计算类
// MARK:- 六、苹果针对浮点类型计算精度问题提供出来的计算类 /// NSDecimalNumberHandler 苹果针对浮点类型计算精度问题提供出来的计算类 /** 初始化方法 roundingMode 舍入方式 scale 小数点后舍入值的位数 raiseOnExactness 精度错误处理 raiseOnOverflow 溢出错误处理 raiseOnUnderflow 下溢错误处理 raiseOnDivideByZero 除以0的错误处理 */ /** public enum RoundingMode : UInt { case plain = 0 是四舍五入 case down = 1 是向下取整 case up = 2 是向上取整 case bankers = 3 是在四舍五入的基础上,加上末尾数为5时,变成偶数的规则 } */ extension String { // MARK: 6.1、+ /// + /// - Parameter strNumber: strNumber description /// - Returns: description public func adding(_ strNumber: String?) -> String { var ln = NSDecimalNumber(string: self) var rn = NSDecimalNumber(string: strNumber) if ln.doubleValue.isNaN { ln = NSDecimalNumber.zero } if rn.doubleValue.isNaN { rn = NSDecimalNumber.zero } let final = ln.adding(rn) return final.stringValue } // MARK: 6.2、- /// - /// - Parameter strNumber: strNumber description /// - Returns: description public func subtracting(_ strNumber: String?) -> String { var ln = NSDecimalNumber(string: self) var rn = NSDecimalNumber(string: strNumber) if ln.doubleValue.isNaN { ln = NSDecimalNumber.zero } if rn.doubleValue.isNaN { rn = NSDecimalNumber.zero } let final = ln.subtracting(rn) return final.stringValue } // MARK: 6.3、* /// ✖️ /// - Parameter strNumber: strNumber description /// - Returns: description public func multiplying(_ strNumber: String?) -> String { var ln = NSDecimalNumber(string: self) var rn = NSDecimalNumber(string: strNumber) if ln.doubleValue.isNaN { ln = NSDecimalNumber.zero } if rn.doubleValue.isNaN { rn = NSDecimalNumber.zero } let final = ln.multiplying(by: rn) return final.stringValue } // MARK: 6.4、/ /// ➗ /// - Parameter strNumber: strNumber description /// - Returns: description public func dividing(_ strNumber: String?) -> String { var ln = NSDecimalNumber(string: self) var rn = NSDecimalNumber(string: strNumber) if ln.doubleValue.isNaN { ln = NSDecimalNumber.zero } if rn.doubleValue.isNaN { rn = NSDecimalNumber.one } if rn.doubleValue == 0 { rn = NSDecimalNumber.one } let final = ln.dividing(by: rn) return final.stringValue } }
八、字符串包含表情的处理
// MARK:- 七、字符串包含表情的处理 extension String { // MARK: 7.1.1、检查字符串是否包含 Emoji 表情 /// 检查字符串是否包含 Emoji 表情 /// - Returns: bool public func containsEmoji() -> Bool { for scalar in unicodeScalars { switch scalar.value { case 0x1F600...0x1F64F, 0x1F300...0x1F5FF, 0x1F680...0x1F6FF, 0x2600...0x26FF, 0x2700...0x27BF, 0xFE00...0xFE0F: return true default: continue } } return false } // MARK: 7.1.2、检查字符串是否包含 Emoji 表情 /// 检查字符串是否包含 Emoji 表情 /// - Returns: bool public func includesEmoji() -> Bool { for i in 0...length { let c: unichar = (self as NSString).character(at: i) if (0xD800 <= c && c <= 0xDBFF) || (0xDC00 <= c && c <= 0xDFFF) { return true } } return false } // MARK: 7.2、去除字符串中的Emoji表情 /// 去除字符串中的Emoji表情 /// - Parameter text: 字符串 /// - Returns: 去除Emoji表情后的字符串 public func deleteEmoji() -> String { do { let regex = try NSRegularExpression(pattern: "[^\\u0020-\\u007E\\u00A0-\\u00BE\\u2E80-\\uA4CF\\uF900-\\uFAFF\\uFE30-\\uFE4F\\uFF00-\\uFFEF\\u0080-\\u009F\\u2000-\\u201f\r\n]", options: NSRegularExpression.Options.caseInsensitive) let modifiedString = regex.stringByReplacingMatches(in: self, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, self.length), withTemplate: "") return modifiedString } catch { JKPrint(error) } return "" } }
九、字符串的一些正则校验
// MARK:- 八、字符串的一些正则校验 extension String { // MARK: 8.1、判断是否全是空白,包括空白字符和换行符号,长度为0返回true /// 判断是否全是空白,包括空白字符和换行符号,长度为0返回true public var isBlank: Bool { return trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines) == "" } // MARK: 8.2、判断是否全十进制数字,长度为0返回false /// 判断是否全十进制数字,长度为0返回false public var isDecimalDigits: Bool { if isEmpty { return false } // 去除什么的操作 return trimmingCharacters(in: NSCharacterSet.decimalDigits) == "" } // MARK: 8.3、判断是否是整数 /// 判断是否是整数 public var isPureInt: Bool { let scan: Scanner = Scanner(string: self) var n: Int = 0 return scan.scanInt(&n) && scan.isAtEnd } // MARK: 8.4、判断是否是Float,此处Float是包含Int的,即Int是特殊的Float /// 判断是否是Float,此处Float是包含Int的,即Int是特殊的Float public var isPureFloat: Bool { let scan: Scanner = Scanner(string: self) var n: Float = 0.0 return scan.scanFloat(&n) && scan.isAtEnd } // MARK: 8.5、判断是否全是字母,长度为0返回false /// 判断是否全是字母,长度为0返回false public var isLetters: Bool { if isEmpty { return false } return trimmingCharacters(in: NSCharacterSet.letters) == "" } // MARK: 8.6、判断是否是中文, 这里的中文不包括数字及标点符号 /// 判断是否是中文, 这里的中文不包括数字及标点符号 public var isChinese: Bool { let mobileRgex = "(^[\u{4e00}-\u{9fef}]+$)" let checker: NSPredicate = NSPredicate(format: "SELF MATCHES %@", mobileRgex) return checker.evaluate(with: self) } // MARK: 8.7、是否是有效昵称,即允许“中文”、“英文”、“数字” /// 是否是有效昵称,即允许“中文”、“英文”、“数字” public var isValidNickName: Bool { let rgex = "(^[\u{4e00}-\u{9faf}_a-zA-Z0-9]+$)" let checker: NSPredicate = NSPredicate(format: "SELF MATCHES %@", rgex) return checker.evaluate(with: self) } // MARK: 8.8、判断是否是有效的手机号码 /// 判断是否是有效的手机号码 public var isValidMobile: Bool { let mobileRgex = "^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199)\\d{8}$" let checker: NSPredicate = NSPredicate(format: "SELF MATCHES %@", mobileRgex) return checker.evaluate(with: self) } // MARK: 8.9、判断是否是有效的电子邮件地址 /// 判断是否是有效的电子邮件地址 public var isValidEmail: Bool { let mobileRgex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}" let checker: NSPredicate = NSPredicate(format: "SELF MATCHES %@", mobileRgex) return checker.evaluate(with: self) } // MARK: 8.10、判断是否有效的身份证号码,不是太严格 /// 判断是否有效的身份证号码,不是太严格 public var isValidIDCardNumber: Bool { let mobileRgex = "^(\\d{15})|((\\d{17})(\\d|[X]))$" let checker: NSPredicate = NSPredicate(format: "SELF MATCHES %@", mobileRgex) return checker.evaluate(with: self) } // MARK: 8.11、严格判断是否有效的身份证号码,检验了省份,生日,校验位,不过没检查市县的编码 /// 严格判断是否有效的身份证号码,检验了省份,生日,校验位,不过没检查市县的编码 public var isValidIDCardNumStrict: Bool { let str = trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines) let len = str.count if !str.isValidIDCardNumber { return false } // 省份代码 let areaArray = ["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"] if !areaArray.contains(str.sub(to: 2)) { return false } var regex = NSRegularExpression() var numberOfMatch = 0 var year = 0 switch len { case 15: // 15位身份证 // 这里年份只有两位,00被处理为闰年了,对2000年是正确的,对1900年是错误的,不过身份证是1900年的应该很少了 year = Int(str.sub(start: 6, length: 2))! if isLeapYear(year: year) { // 闰年 do { // 检测出生日期的合法性 regex = try NSRegularExpression(pattern: "^[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}$", options: .caseInsensitive) } catch {} } else { do { // 检测出生日期的合法性 regex = try NSRegularExpression(pattern: "^[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}$", options: .caseInsensitive) } catch {} } numberOfMatch = regex.numberOfMatches(in: str, options: NSRegularExpression.MatchingOptions.reportProgress, range: NSMakeRange(0, len)) if numberOfMatch > 0 { return true } else { return false } case 18: // 18位身份证 year = Int(str.sub(start: 6, length: 4))! if isLeapYear(year: year) { // 闰年 do { // 检测出生日期的合法性 regex = try NSRegularExpression(pattern: "^[1-9][0-9]{5}19[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}[0-9Xx]$", options: .caseInsensitive) } catch {} } else { do { // 检测出生日期的合法性 regex = try NSRegularExpression(pattern: "^[1-9][0-9]{5}19[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}[0-9Xx]$", options: .caseInsensitive) } catch {} } numberOfMatch = regex.numberOfMatches(in: str, options: NSRegularExpression.MatchingOptions.reportProgress, range: NSMakeRange(0, len)) if numberOfMatch > 0 { var s = 0 let jiaoYan = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3] for i in 0 ..< 17 { if let d = Int(str.slice(i ..< (i + 1))) { s += d * jiaoYan[i % 10] } else { return false } } let Y = s % 11 let JYM = "10X98765432" let M = JYM.sub(start: Y, length: 1) if M == str.sub(start: 17, length: 1) { return true } else { return false } } else { return false } default: return false } } // MARK: 8.12、校验字符串位置是否合理,并返回String.Index /// 校验字符串位置是否合理,并返回String.Index /// - Parameter original: 位置 /// - Returns: String.Index public func validIndex(original: Int) -> String.Index { switch original { case ...startIndex.utf16Offset(in: self): return startIndex case endIndex.utf16Offset(in: self)...: return endIndex default: return index(startIndex, offsetBy: original) } } // MARK: 8.13、隐藏手机号中间的几位 /// 隐藏手机号中间的几位 /// - Parameter combine: 隐藏的字符串(替换的类型) /// - Returns: 返回隐藏的手机号 public func hidePhone(combine: String = "****") -> String { if self.count >= 11 { let pre = self.sub(start: 0, length: 3) let post = self.sub(start: 7, length: 4) return pre + combine + post } else { return self } } // MARK:- private 方法 // MARK: 是否是闰年 /// 是否是闰年 /// - Parameter year: 年份 /// - Returns: 返回是否是闰年 private func isLeapYear(year: Int) -> Bool { if year % 400 == 0 { return true } else if year % 100 == 0 { return false } else if year % 4 == 0 { return true } else { return false } } }
十、字符串截取的操作
// MARK:- 九、字符串截取的操作 extension String { // MARK: 9.1、截取字符串从开始到 index /// 截取字符串从开始到 index /// - Parameter index: 截取到的位置 /// - Returns: 截取后的字符串 public func sub(to index: Int) -> String { let end_Index = validIndex(original: index) return String(self[startIndex ..< end_Index]) } // MARK: 9.2、截取字符串从index到结束 /// 截取字符串从index到结束 /// - Parameter index: 截取结束的位置 /// - Returns: 截取后的字符串 public func sub(from index: Int) -> String { let start_index = validIndex(original: index) return String(self[start_index ..< endIndex]) } // MARK: 9.3、获取指定位置和长度的字符串 /// 获取指定位置和大小的字符串 /// - Parameters: /// - start: 开始位置 /// - length: 长度 /// - Returns: 截取后的字符串 public func sub(start: Int, length: Int = -1) -> String { var len = length if len == -1 { len = count - start } let st = index(startIndex, offsetBy: start) let en = index(st, offsetBy: len) let range = st ..< en return String(self[range]) // .substring(with:range) } // MARK: 9.4、切割字符串(区间范围 前闭后开) /** https://blog.csdn.net/wang631106979/article/details/54098910 CountableClosedRange:可数的闭区间,如 0...2 CountableRange:可数的开区间,如 0..<2 ClosedRange:不可数的闭区间,如 0.1...2.1 Range:不可数的开居间,如 0.1..<2.1 */ /// 切割字符串(区间范围 前闭后开) /// - Parameter range: 范围 /// - Returns: 切割后的字符串 public func slice(_ range: CountableRange<Int>) -> String { // 如 sliceString(2..<6) /** upperBound(上界) lowerBound(下界) */ let startIndex = validIndex(original: range.lowerBound) let endIndex = validIndex(original: range.upperBound) guard startIndex < endIndex else { return "" } return String(self[startIndex ..< endIndex]) } // MARK: 9.5、用整数返回子字符串开始的位置 /// 用整数返回子字符串开始的位置 /// - Parameter sub: 字符串 /// - Returns: 返回字符串的位置 public func position(of sub: String) -> Int { if sub.isEmpty { return 0 } var pos = -1 if let range = self.range(of: sub) { if !range.isEmpty { pos = distance(from: startIndex, to: range.lowerBound) } } return pos } }
十、字符串编码的处理
// MARK:- 十、字符串编码的处理 extension String { // MARK: 10.1、特殊字符编码处理urlEncoded /// url编码 默认urlQueryAllowed public func urlEncoding(characters: CharacterSet = .urlQueryAllowed) -> String { let encodeUrlString = self.addingPercentEncoding(withAllowedCharacters: characters) return encodeUrlString ?? "" } // MARK:- 10.2、url编码 Alamofire AFNetworking 处理方式 推荐使用 /// url编码 Alamofire AFNetworking 处理方式 推荐使用 public var urlEncoded: String { // does not include "?" or "/" due to RFC 3986 - Section 3.4 let generalDelimitersToEncode = ":#[]@" let subDelimitersToEncode = "!$&'()*+,;=" var allowedCharacterSet = CharacterSet.urlQueryAllowed allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)") let encodeUrlString = self.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) return encodeUrlString ?? "" } // MARK: 10.3、url编码 会对所有特殊字符做编码 特殊情况下使用 /// url编码 会对所有特殊字符做编码 特殊情况下使用 public var urlAllEncoded: String { let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4 let subDelimitersToEncode = "!$&'()*+,;=/?_-.~" var allowedCharacterSet = CharacterSet.urlQueryAllowed allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)") let encodeUrlString = self.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) return encodeUrlString ?? "" } }