JKSwiftExtension磷杏,測試用例在 StringExtensionViewController 里面
目錄:
1、字符串基本的擴展
2帜慢、沙盒路徑的獲取
3、iOS CharacterSet(字符集)
4、字符串的轉(zhuǎn)換
5、字符串UI的處理
6膛堤、字符串有關(guān)數(shù)字方面的擴展
7手趣、蘋果針對浮點類型計算精度問題提供出來的計算類
8晌该、字符串包含表情的處理
9、字符串的一些正則校驗
10绿渣、字符串截取的操作
11朝群、字符串編碼的處理
一、字符串基本的擴展
// MARK:- 0:字符串基本的擴展
public extension String {
// MARK: 0.1中符、字符串的長度
/// 字符串的長度
var length: Int {
return self.count
}
// MARK: 0.2姜胖、判斷是否包含某個子串
/// 判斷是否包含某個子串
/// - Parameter find: 子串
/// - Returns: Bool
func contains(find: String) -> Bool {
return self.range(of: find) != nil
}
// MARK: 0.3、判斷是否包含某個子串 -- 忽略大小寫
/// 判斷是否包含某個子串 -- 忽略大小寫
/// - Parameter find: 子串
/// - Returns: Bool
func containsIgnoringCase(find: String) -> Bool {
return self.range(of: find, options: .caseInsensitive) != nil
}
// MARK: 0.4淀散、字符串轉(zhuǎn) Base64
/// 字符串轉(zhuǎn) Base64
var base64: String? {
guard let plainData = (self as NSString).data(using: String.Encoding.utf8.rawValue) else {
return nil
}
let base64String = plainData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
return base64String
}
// MARK: 0.5右莱、將16進制字符串轉(zhuǎn)為Int
/// 將16進制字符串轉(zhuǎn)為Int
var hexInt: Int {
return Int(self, radix: 16) ?? 0
}
// MARK: 0.6、判斷是不是九宮格鍵盤
/// 判斷是不是九宮格鍵盤
func isNineKeyBoard() -> Bool {
let other : NSString = "????????"
let len = self.count
for _ in 0..<len {
if !(other.range(of: self).location != NSNotFound) {
return false
}
}
return true
}
// MARK: 0.7档插、字符串轉(zhuǎn) UIViewController
/// 字符串轉(zhuǎn) UIViewController
/// - Returns: 對應(yīng)的控制器
@discardableResult
func toViewController() -> UIViewController? {
// 1.動態(tài)獲取命名空間
let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String
// 2.將字符串轉(zhuǎn)換為類
// 2.1.默認(rèn)情況下命名空間就是項目的名稱慢蜓,但是命名空間的名稱是可以更改的
guard let Class: AnyClass = NSClassFromString(namespace + "." + self) else {
return nil
}
// 3.通過類創(chuàng)建對象
// 3.1.將AnyClass 轉(zhuǎn)化為指定的類
let vcClass = Class as! UIViewController.Type
// 4.通過class創(chuàng)建對象
let vc = vcClass.init()
return vc
}
// MARK: 0.8、字符串轉(zhuǎn) AnyClass
/// 字符串轉(zhuǎn) AnyClass
/// - Returns: 對應(yīng)的 Class
@discardableResult
func toClass() -> AnyClass? {
// 1.動態(tài)獲取命名空間
let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String
// 2.將字符串轉(zhuǎn)換為類
// 2.1.默認(rèn)情況下命名空間就是項目的名稱郭膛,但是命名空間的名稱是可以更改的
guard let Class: AnyClass = NSClassFromString(namespace + "." + self) else {
return nil
}
return Class
}
}
二晨抡、沙盒路徑的獲取
// MARK:- 1、沙盒路徑的獲取
/*
- 1、Home(應(yīng)用程序包)目錄
- 整個應(yīng)用程序各文檔所在的目錄,包含了所有的資源文件和可執(zhí)行文件
- 2耘柱、Documents
- 保存應(yīng)用運行時生成的需要持久化的數(shù)據(jù)如捅,iTunes同步設(shè)備時會備份該目錄
- 需要保存由"應(yīng)用程序本身"產(chǎn)生的文件或者數(shù)據(jù),例如: 游戲進度调煎,涂鴉軟件的繪圖
- 目錄中的文件會被自動保存在 iCloud
- 注意: 不要保存從網(wǎng)絡(luò)上下載的文件镜遣,否則會無法上架!
- 3、Library
- 3.1士袄、Library/Cache
- 保存應(yīng)用運行時生成的需要持久化的數(shù)據(jù)烈涮,iTunes同步設(shè)備時不備份該目錄。一般存放體積大窖剑、不需要備份的非重要數(shù)據(jù)
- 保存臨時文件,"后續(xù)需要使用"坚洽,例如: 緩存的圖片,離線數(shù)據(jù)(地圖數(shù)據(jù))
- 系統(tǒng)不會清理 cache 目錄中的文件
- 就要求程序開發(fā)時, "必須提供 cache 目錄的清理解決方案"
- 3.2西土、Library/Preference
- 保存應(yīng)用的所有偏好設(shè)置讶舰,IOS的Settings應(yīng)用會在該目錄中查找應(yīng)用的設(shè)置信息。iTunes
- 用戶偏好需了,使用 NSUserDefault 直接讀寫跳昼!
- 如果想要數(shù)據(jù)及時寫入硬盤,還需要調(diào)用一個同步方法
- 4肋乍、tmp
- 保存臨時文件鹅颊,"后續(xù)不需要使用"
- tmp 目錄中的文件,系統(tǒng)會自動被清空
- 重新啟動手機, tmp 目錄會被清空
- 系統(tǒng)磁盤空間不足時墓造,系統(tǒng)也會自動清理
- 保存應(yīng)用運行時所需要的臨時數(shù)據(jù)堪伍,使用完畢后再將相應(yīng)的文件從該目錄刪除。應(yīng)用沒有運行觅闽,系統(tǒng)也可能會清除該目錄下的文件帝雇,iTunes不會同步備份該目錄
*/
public extension String {
// MARK: 1.1、獲取Home的完整路徑名
/// 獲取Home的完整路徑名
/// - Returns: Home的完整路徑名
static func homeDirectory() -> String {
//獲取程序的Home目錄
let homeDirectory = NSHomeDirectory()
return homeDirectory
}
// MARK: 1.2蛉拙、獲取Documnets的完整路徑名
/// 獲取Documnets的完整路徑名
/// - Returns: Documnets的完整路徑名
static func DocumnetsDirectory() -> String {
//獲取程序的documentPaths目錄
//方法1
// let documentPaths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
// let documnetPath = documentPaths[0]
//方法2
let ducumentPath = NSHomeDirectory() + "/Documents"
return ducumentPath
}
// MARK: 1.3尸闸、獲取Library的完整路徑名
/**
這個目錄下有兩個子目錄:Caches 和 Preferences
Library/Preferences目錄,包含應(yīng)用程序的偏好設(shè)置文件孕锄。不應(yīng)該直接創(chuàng)建偏好設(shè)置文件吮廉,而是應(yīng)該使用NSUserDefaults類來取得和設(shè)置應(yīng)用程序的偏好。
Library/Caches目錄畸肆,主要存放緩存文件宦芦,iTunes不會備份此目錄,此目錄下文件不會再應(yīng)用退出時刪除
*/
/// 獲取Library的完整路徑名
/// - Returns: Library的完整路徑名
static func LibraryDirectory() -> String {
//獲取程序的documentPaths目錄
//Library目錄-方法1
// let libraryPaths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.libraryDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
// let libraryPath = libraryPaths[0]
//
// Library目錄-方法2
let libraryPath = NSHomeDirectory() + "/Library"
return libraryPath
}
// MARK: 1.4恼除、獲取/Library/Caches的完整路徑名
/// 獲取/Library/Caches的完整路徑名
/// - Returns: /Library/Caches的完整路徑名
static func CachesDirectory() -> String {
//獲取程序的/Library/Caches目錄
let cachesPath = NSHomeDirectory() + "/Library/Caches"
return cachesPath
}
// MARK: 1.5踪旷、獲取Library/Preferences的完整路徑名
/// 獲取Library/Preferences的完整路徑名
/// - Returns: Library/Preferences的完整路徑名
static func PreferencesDirectory() -> String {
//Library/Preferences目錄-方法2
let preferencesPath = NSHomeDirectory() + "/Library/Preferences"
return preferencesPath
}
// MARK: 1.6曼氛、獲取Tmp的完整路徑名
/// 獲取Tmp的完整路徑名,用于存放臨時文件令野,保存應(yīng)用程序再次啟動過程中不需要的信息舀患,重啟后清空。
/// - Returns: Tmp的完整路徑名
static func TmpDirectory() -> String {
//方法1
//let tmpDir = NSTemporaryDirectory()
//方法2
let tmpDir = NSHomeDirectory() + "/tmp"
return tmpDir
}
}
三气破、iOS CharacterSet(字符集)
// MARK:- 二聊浅、iOS CharacterSet(字符集)
/**
CharacterSet是在Foundation框架下的一個結(jié)構(gòu)體,用于搜索操作的一組Unicode字符值现使。官方的API地址:https://developer.apple.com/documentation/foundation/characterset
概述
字符集表示一組符合unicode的字符低匙。基礎(chǔ)類型使用字符集將字符組合在一起進行搜索操作碳锈,以便在搜索期間可以找到任何特定的字符集顽冶。
這種類型提供了“寫時復(fù)制”的行為,并且還連接到Objective-C NSCharacterSet類售碳。
總之就是將unicode字符强重,按組分類,便于搜索查找贸人,驗證字符串间景。通常我們在一些場景下會用到一個字符串是否包含某種特定字符,比如判斷密碼是否只包含數(shù)字艺智,檢查url是否有不規(guī)范字符倘要,刪除多余空格等操作
屬性 描述
CharacterSet.alphanumerics
controlCharacters: 控制符
whitespaces: 空格
whitespacesAndNewlines: 空格和換行
decimalDigits: 0-9的數(shù)字,也不包含小數(shù)點
letters: 所有英文字母十拣,包含大小寫 65-90 97-122
lowercaseLetters: 小寫英文字母 97-122
uppercaseLetters: 大寫英文字母 65-90
nonBaseCharacters: 非基礎(chǔ)字符 M*
alphanumerics: 字母和數(shù)字的組合封拧,包含大小寫, 不包含小數(shù)點
decomposables: 可分解
illegalCharacters: 不合規(guī)字符,沒有在Unicode 3.2 標(biāo)準(zhǔn)中定義的字符
punctuationCharacters: 標(biāo)點符號父晶,連接線哮缺,引號什么的 P*
capitalizedLetters: 字母,首字母大寫甲喝,Lt類別
symbols: 符號,包含S* 所有內(nèi)容铛只,運算符埠胖,貨幣符號什么的
newlines: 返回一個包含換行符的字符集,U+000A ~ U+000D, U+0085, U+2028, and U+2029
urlUserAllowed:
urlPasswordAllowed:
urlHostAllowed:
urlPathAllowed:
urlQueryAllowed:
urlFragmentAllowed:
bitmapRepresentation:
inverted: 相反的字符集淳玩。例如 CharacterSet.whitespaces.inverted 就是沒有空格
*/
public extension String {
// MARK: 2.1直撤、去除字符串前后的 空格
/// 去除字符串前后的換行和換行
var removeBeginEndAllSapcefeed: String {
let resultString = self.trimmingCharacters(in: CharacterSet.whitespaces)
return resultString
}
// MARK: 2.2、去除字符串前后的 換行
/// 去除字符串前后的 換行
var removeBeginEndAllLinefeed: String {
let resultString = self.trimmingCharacters(in: CharacterSet.newlines)
return resultString
}
// MARK: 2.3蜕着、去除字符串前后的 換行和空格
/// 去除字符串前后的 換行和空格
var removeBeginEndAllSapceAndLinefeed: String {
var resultString = self.trimmingCharacters(in: CharacterSet.whitespaces)
resultString = resultString.trimmingCharacters(in: CharacterSet.newlines)
return resultString
}
// MARK: 2.4谋竖、去掉所有空格
/// 去掉所有空格
var removeAllSapce: String {
return replacingOccurrences(of: " ", with: "", options: .literal, range: nil)
}
// MARK: 2.5红柱、去掉所有換行
/// 去掉所有換行
var removeAllLinefeed: String {
return replacingOccurrences(of: "\n", with: "", options: .literal, range: nil)
}
// MARK: 2.6、去掉所有空格 和 換行
/// 去掉所有的空格 和 換行
var removeAllLineAndSapcefeed: String {
// 去除所有的空格
var resultString = replacingOccurrences(of: " ", with: "", options: .literal, range: nil)
// 去除所有的換行
resultString = resultString.replacingOccurrences(of: "\n", with: "", options: .literal, range: nil)
return resultString
}
// MARK: 2.7蓖乘、是否是 0-9 的數(shù)字锤悄,也不包含小數(shù)點
/// 是否是 0-9 的數(shù)字,也不包含小數(shù)點
/// - Returns: 結(jié)果
func isValidNumber() -> Bool {
/// 0-9的數(shù)字嘉抒,也不包含小數(shù)點
let rst: String = self.trimmingCharacters(in: .decimalDigits)
if rst.count > 0 {
return false
}
return true
}
// MARK: 2.8零聚、url進行編碼
/// url 進行編碼
/// - Returns: 返回對應(yīng)的URL
func urlValidate() -> URL {
return URL(string: self.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlHostAllowed) ?? "")!
}
// MARK: 2.9、某個字符使用某個字符替換掉
/// 某個字符使用某個字符替換掉
/// - Parameters:
/// - removeString: 原始字符
/// - replacingString: 替換后的字符
/// - Returns: 替換后的整體字符串
func removeSomeStringUseSomeString(removeString: String, replacingString: String = "") -> String {
return replacingOccurrences(of: removeString, with: replacingString)
}
// MARK: 2.10些侍、使用正則表達式替換某些子串
/// 使用正則表達式替換
/// - Parameters:
/// - pattern: 正則
/// - with: 用來替換的字符串
/// - options: 策略
/// - Returns: 返回替換后的字符串
func pregReplace(pattern: String, with: String,
options: NSRegularExpression.Options = []) -> String {
let regex = try! NSRegularExpression(pattern: pattern, options: options)
return regex.stringByReplacingMatches(in: self, options: [],
range: NSMakeRange(0, self.count),
withTemplate: with)
}
// MARK: 2.11隶症、刪除指定的字符
/// 刪除指定的字符
/// - Parameter characterString: 指定的字符
/// - Returns: 返回刪除后的字符
func removeCharacter(characterString: String) -> String {
let characterSet = CharacterSet(charactersIn: characterString)
return trimmingCharacters(in: characterSet)
}
}
四、字符串的轉(zhuǎn)換
// MARK:- 三岗宣、字符串的轉(zhuǎn)換
public extension String {
// MARK: 3.1蚂会、字符串 轉(zhuǎn) CGFloat
/// 字符串 轉(zhuǎn) Float
/// - Returns: CGFloat
func toCGFloat() -> CGFloat? {
if let doubleValue = Double(self) {
return CGFloat(doubleValue)
}
return nil
}
// MARK: 3.2、字符串轉(zhuǎn) bool
/// 字符串轉(zhuǎn) bool
var bool: Bool? {
switch self.lowercased() {
case "true", "t", "yes", "y", "1":
return true
case "false", "f", "no", "n", "0":
return false
default:
return nil
}
}
// MARK: 3.3耗式、字符串轉(zhuǎn) Int
/// 字符串轉(zhuǎn) Int
/// - Returns: Int
func toInt() -> Int? {
if let num = NumberFormatter().number(from: self) {
return num.intValue
} else {
return nil
}
}
// MARK: 3.4颂龙、字符串轉(zhuǎn) Double
/// 字符串轉(zhuǎn) Double
/// - Returns: Double
func toDouble() -> Double? {
if let num = NumberFormatter().number(from: self) {
return num.doubleValue
} else {
return nil
}
}
// MARK: 3.5、字符串轉(zhuǎn) Float
/// 字符串轉(zhuǎn) Float
/// - Returns: Float
func toFloat() -> Float? {
if let num = NumberFormatter().number(from: self) {
return num.floatValue
} else {
return nil
}
}
// MARK: 3.6纽什、字符串轉(zhuǎn) Bool
/// 字符串轉(zhuǎn) Bool
/// - Returns: Bool
func toBool() -> Bool? {
let trimmedString = lowercased()
if trimmedString == "true" || trimmedString == "false" {
return (trimmedString as NSString).boolValue
}
return nil
}
// MARK: 3.7措嵌、字符串轉(zhuǎn) NSString
/// 字符串轉(zhuǎn) NSString
var toNSString: NSString {
return self as NSString
}
}
五、字符串UI的處理
// MARK:- 四芦缰、字符串UI的處理
extension String {
// MARK: 4.1企巢、對字符串(多行)指定出字體大小和最大的 Size,獲取 (Size)
/// 對字符串(多行)指定出字體大小和最大的 Size让蕾,獲取展示的 Size
/// - Parameters:
/// - font: 字體大小
/// - size: 字符串的最大寬和高
/// - Returns: 按照 font 和 Size 的字符的Size
public func rectSize(font: UIFont, size: CGSize) -> CGSize {
let attributes = [NSAttributedString.Key.font: font]
/**
usesLineFragmentOrigin: 整個文本將以每行組成的矩形為單位計算整個文本的尺寸
usesFontLeading:
usesDeviceMetrics:
@available(iOS 6.0, *)
truncatesLastVisibleLine:
*/
let option = NSStringDrawingOptions.usesLineFragmentOrigin
let rect: CGRect = self.boundingRect(with: size, options: option, attributes: attributes, context: nil)
return rect.size
}
// MARK: 4.2浪规、對字符串(多行)指定字體及Size,獲取 (高度)
/// 對字符串指定字體及Size探孝,獲取 (高度)
/// - Parameters:
/// - font: 字體的大小
/// - size: 字體的size
/// - Returns: 返回對應(yīng)字符串的高度
public func rectHeight(font: UIFont, size: CGSize) -> CGFloat {
return rectSize(font: font, size: size).height
}
// MARK: 4.3笋婿、對字符串(多行)指定字體及Size,獲取 (寬度)
/// 對字符串指定字體及Size顿颅,獲取 (寬度)
/// - Parameters:
/// - font: 字體的大小
/// - size: 字體的size
/// - Returns: 返回對應(yīng)字符串的寬度
public func rectWidth(font: UIFont, size: CGSize) -> CGFloat {
return rectSize(font: font, size: size).width
}
// MARK: 4.4缸濒、對字符串(單行)指定字體,獲取 (Size)
/// 對字符串(單行)指定字體粱腻,獲取 (Size)
/// - Parameter font: 字體的大小
/// - Returns: 返回單行字符串的 size
public func singleLineSize(font: UIFont) -> CGSize {
let attrs = [NSAttributedString.Key.font: font]
return self.size(withAttributes: attrs as [NSAttributedString.Key: Any])
}
// MARK: 4.5庇配、對字符串(單行)指定字體,獲取 (width)
/// 對字符串(單行)指定字體绍些,獲取 (width)
/// - Parameter font: 字體的大小
/// - Returns: 返回單行字符串的 width
public func singleLineWidth(font: UIFont) -> CGFloat {
let attrs = [NSAttributedString.Key.font: font]
return self.size(withAttributes: attrs as [NSAttributedString.Key: Any]).width
}
// MARK: 4.6捞慌、對字符串(單行)指定字體,獲取 (Height)
/// 對字符串(單行)指定字體柬批,獲取 (height)
/// - Parameter font: 字體的大小
/// - Returns: 返回單行字符串的 height
public func singleLineHeight(font: UIFont) -> CGFloat {
let attrs = [NSAttributedString.Key.font: font]
return self.size(withAttributes: attrs as [NSAttributedString.Key: Any]).height
}
// MARK: 4.7啸澡、字符串通過 label 根據(jù)高度&字體 —> Size
/// 字符串通過 label 根據(jù)高度&字體 ——> Size
/// - Parameters:
/// - height: 字符串最大的高度
/// - font: 字體大小
/// - Returns: 返回Size
public func sizeAccording(width: CGFloat, height: CGFloat = CGFloat(MAXFLOAT), font: UIFont) -> CGSize {
if self.isBlank {return CGSize(width: 0, height: 0)}
let rect = CGRect(x: 0, y: 0, width: width, height: height)
let label = UILabel(frame: rect).font(font).text(self).line(0)
return label.sizeThatFits(rect.size)
}
// MARK: 4.8袖订、字符串通過 label 根據(jù)高度&字體 —> Width
/// 字符串通過 label 根據(jù)高度&字體 ——> Width
/// - Parameters:
/// - height: 字符串最大高度
/// - font: 字體大小
/// - Returns: 返回寬度大小
public func widthAccording(width: CGFloat, height: CGFloat = CGFloat(MAXFLOAT), font: UIFont) -> CGFloat {
if self.isBlank {return 0}
let rect = CGRect(x: 0, y: 0, width: width, height: height)
let label = UILabel(frame: rect).font(font).text(self).line(0)
return label.sizeThatFits(rect.size).width
}
// MARK: 4.9、字符串通過 label 根據(jù)寬度&字體 —> height
/// 字符串通過 label 根據(jù)寬度&字體 ——> height
/// - Parameters:
/// - width: 字符串最大寬度
/// - font: 字體大小
/// - Returns: 返回高度大小
public func heightAccording(width: CGFloat, height: CGFloat = CGFloat(MAXFLOAT), font: UIFont) -> CGFloat {
if self.isBlank {return 0}
let rect = CGRect(x: 0, y: 0, width: width, height: height)
let label = UILabel(frame: rect).font(font).text(self).line(0)
return label.sizeThatFits(rect.size).height
}
// MARK: 4.10嗅虏、字符串根據(jù)寬度 & 字體 & 行間距 —> Size
/// 字符串根據(jù)寬度 & 字體 & 行間距 ——> Size
/// - Parameters:
/// - width: 字符串最大的寬度
/// - heiht: 字符串最大的高度
/// - font: 字體的大小
/// - lineSpacing: 行間距
/// - Returns: 返回對應(yīng)的size
public func sizeAccording(width: CGFloat, height: CGFloat = CGFloat(MAXFLOAT), font: UIFont, lineSpacing: CGFloat) -> CGSize {
if self.isBlank {return CGSize(width: 0, height: 0)}
let rect = CGRect(x: 0, y: 0, width: width, height: CGFloat(MAXFLOAT))
let label = UILabel(frame: rect).font(font).text(self).line(0)
let attrStr = NSMutableAttributedString(string: self)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineSpacing
attrStr.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, self.count))
label.attributedText = attrStr
return label.sizeThatFits(rect.size)
}
// MARK: 4.11洛姑、字符串根據(jù)寬度 & 字體 & 行間距 —> width
/// 字符串根據(jù)寬度 & 字體 & 行間距 ——> width
/// - Parameters:
/// - width: 字符串最大的寬度
/// - heiht: 字符串最大的高度
/// - font: 字體的大小
/// - lineSpacing: 行間距
/// - Returns: 返回對應(yīng)的 width
public func widthAccording(width: CGFloat, height: CGFloat = CGFloat(MAXFLOAT), font: UIFont, lineSpacing: CGFloat) -> CGFloat {
if self.isBlank {return 0}
let rect = CGRect(x: 0, y: 0, width: width, height: height)
let label = UILabel(frame: rect).font(font).text(self).line(0)
let attrStr = NSMutableAttributedString(string: self)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineSpacing
attrStr.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, self.count))
label.attributedText = attrStr
return label.sizeThatFits(rect.size).width
}
// MARK: 4.12、字符串根據(jù)寬度 & 字體 & 行間距 —> height
/// 字符串根據(jù)寬度 & 字體 & 行間距 ——> height
/// - Parameters:
/// - width: 字符串最大的寬度
/// - heiht: 字符串最大的高度
/// - font: 字體的大小
/// - lineSpacing: 行間距
/// - Returns: 返回對應(yīng)的 height
public func heightAccording(width: CGFloat, height: CGFloat = CGFloat(MAXFLOAT), font: UIFont, lineSpacing: CGFloat) -> CGFloat {
if self.isBlank {return 0}
let rect = CGRect(x: 0, y: 0, width: width, height: height)
let label = UILabel(frame: rect).font(font).text(self).line(0)
let attrStr = NSMutableAttributedString(string: self)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineSpacing
attrStr.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSMakeRange(0, self.count))
label.attributedText = attrStr
return label.sizeThatFits(rect.size).height
}
}
六旋恼、字符串有關(guān)數(shù)字方面的擴展
// MARK:- 五吏口、字符串有關(guān)數(shù)字方面的擴展
public enum StringCutType {
case normal, auto
}
public extension String {
// MARK: 5.1、將金額字符串轉(zhuǎn)化為帶逗號的金額 按照千分位劃分冰更,如 "1234567" => 1,234,567 1234567.56 => 1,234,567.56
/// 將金額字符串轉(zhuǎn)化為帶逗號的金額 按照千分位劃分产徊,如 "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、字符串差不多精確轉(zhuǎn)換成Double——之所以差不多蜀细,是因為有精度損失
/// - Important: 字符串差不多精確轉(zhuǎn)換成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、去掉小數(shù)點后多余的0
/// 去掉小數(shù)點后多余的0
/// - Returns: 返回小數(shù)點后沒有 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奠衔、將數(shù)字的字符串處理成 幾位 位小數(shù)的情況
/// 將數(shù)字的字符串處理成 幾位 位小數(shù)的情況
/// - Parameters:
/// - numberDecimal: 保留幾位小數(shù)
/// - mode: 模式
/// - Returns: 返回保留后的小數(shù)谆刨,如果是非字符,則根據(jù)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
// 小數(shù)位最多位數(shù)
formatter.maximumFractionDigits = numberDecimal
// 小數(shù)位最少位數(shù)
formatter.minimumFractionDigits = numberDecimal
// 整數(shù)位最少位數(shù)
formatter.minimumIntegerDigits = 1
// 整數(shù)位最多位數(shù)
formatter.maximumIntegerDigits = 100
// 獲取結(jié)果
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 小數(shù)點后舍入值的位數(shù)
raiseOnExactness 精度錯誤處理
raiseOnOverflow 溢出錯誤處理
raiseOnUnderflow 下溢錯誤處理
raiseOnDivideByZero 除以0的錯誤處理
*/
/**
public enum RoundingMode : UInt {
case plain = 0 是四舍五入
case down = 1 是向下取整
case up = 2 是向上取整
case bankers = 3 是在四舍五入的基礎(chǔ)上,加上末尾數(shù)為5時脏里,變成偶數(shù)的規(guī)則
}
*/
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徘公、判斷是否全十進制數(shù)字,長度為0返回false
/// 判斷是否全十進制數(shù)字哮针,長度為0返回false
public var isDecimalDigits: Bool {
if isEmpty {
return false
}
// 去除什么的操作
return trimmingCharacters(in: NSCharacterSet.decimalDigits) == ""
}
// MARK: 8.3关面、判斷是否是整數(shù)
/// 判斷是否是整數(shù)
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、判斷是否是中文, 這里的中文不包括數(shù)字及標(biāo)點符號
/// 判斷是否是中文, 這里的中文不包括數(shù)字及標(biāo)點符號
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瞻想、是否是有效昵稱压真,即允許“中文”、“英文”蘑险、“數(shù)字”
/// 是否是有效昵稱滴肿,即允許“中文”、“英文”佃迄、“數(shù)字”
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呵俏、判斷是否有效的身份證號碼堆缘,不是太嚴(yán)格
/// 判斷是否有效的身份證號碼,不是太嚴(yán)格
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普碎、嚴(yán)格判斷是否有效的身份證號碼,檢驗了省份吼肥,生日,校驗位随常,不過沒檢查市縣的編碼
/// 嚴(yán)格判斷是否有效的身份證號碼,檢驗了省份潜沦,生日,校驗位绪氛,不過沒檢查市縣的編碼
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年的應(yīng)該很少了
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到結(jié)束
/// 截取字符串從index到結(jié)束
/// - Parameter index: 截取結(jié)束的位置
/// - 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稽揭、切割字符串(區(qū)間范圍 前閉后開)
/**
https://blog.csdn.net/wang631106979/article/details/54098910
CountableClosedRange:可數(shù)的閉區(qū)間,如 0...2
CountableRange:可數(shù)的開區(qū)間肥卡,如 0..<2
ClosedRange:不可數(shù)的閉區(qū)間溪掀,如 0.1...2.1
Range:不可數(shù)的開居間,如 0.1..<2.1
*/
/// 切割字符串(區(qū)間范圍 前閉后開)
/// - 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步鉴、用整數(shù)返回子字符串開始的位置
/// 用整數(shù)返回子字符串開始的位置
/// - 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編碼 默認(rèn)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 ?? ""
}
}