Visual_Novel_iOS/crush/Crush/Src/Utils/Extensions/StringExt.swift

422 lines
15 KiB
Swift
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// UIWindow+Ext.swift
// DouYinSwift5
//
// Created by lym on 2020/7/23.
// Copyright © 2020 lym. All rights reserved.
//
import Foundation
import UIKit
public extension String {
static func randomString(_ length: Int) -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return String((0 ..< length).map { _ in letters.randomElement()! })
}
init(deviceToken: Data) {
self = deviceToken.map { String(format: "%.2hhx", $0) }.joined()
}
///
// func contains(_ regular: String) -> Bool {
// return range(of: regular, options: .regularExpression, range: nil, locale: nil) != nil
// }
///
var trimmed: String {
return trimmingCharacters(in: .whitespacesAndNewlines)
}
func removeSpaceAndNewLine() -> String {
return trimmingCharacters(in: .whitespacesAndNewlines)
}
var removingAllWhitespace: String {
return replacingOccurrences(of: " ", with: "")
}
///
///
/// "", " ", "\n", " \n "
/// true false
var isNotBlank: Bool {
return !trimmed.isEmpty
}
///
var rangeOfAll: NSRange {
return NSRange(location: 0, length: count)
}
/// nil ""
static func realEmpty(str: String?) -> Bool {
return (str == nil || str == "")
}
/// JSON
func toDictionary() -> [String: Any]? {
guard let jsonData = data(using: .utf8),
let dict = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)
else {
return nil
}
return (dict as! [String: Any])
}
/// JSON Array
func jsonStringToArray() -> [Any]? {
guard let jsonData: Data = data(using: .utf8),
let array = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)
else {
return nil
}
return (array as! [Any])
}
///
@discardableResult
func matchStrRange(_ matchStr: String) -> [NSRange] {
var selfStr = self as NSString
var withStr = Array(repeating: "X", count: (matchStr as NSString).length).joined(separator: "") //
if matchStr == withStr { withStr = withStr.lowercased() } //
var allRange = [NSRange]()
while selfStr.range(of: matchStr).location != NSNotFound {
let range = selfStr.range(of: matchStr)
allRange.append(NSRange(location: range.location, length: range.length))
selfStr = selfStr.replacingCharacters(in: NSMakeRange(range.location, range.length), with: withStr) as NSString
}
return allRange
}
///
static func randomAlphaNumericString(length: Int) -> String {
let allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let allowedCharsCount = UInt32(allowedChars.count)
var randomString = ""
for _ in 0 ..< length {
let randomNum = Int(arc4random_uniform(allowedCharsCount))
let randomIndex = allowedChars.index(allowedChars.startIndex, offsetBy: randomNum)
let newCharacter = allowedChars[randomIndex]
randomString += String(newCharacter)
}
return randomString
}
///
static func randomNumber(length: Int) -> String {
var result = ""
for _ in 0..<length {
let digit = Int.random(in: 0...9)
result.append("\(digit)")
}
return result
}
///
func localized(_ bundle: Bundle = .main, tableName: String = "Localizable") -> String {
return NSLocalizedString(self, tableName: tableName, bundle: bundle, comment: "")
}
}
// MAKR :
//
struct SplitedResult {
let fragment: String
let isMatched: Bool
let captures: [String?]
}
extension String {
///
func split(
usingRegex pattern: String,
options: NSRegularExpression.Options = .dotMatchesLineSeparators
) -> [SplitedResult] {
do {
let regex = try NSRegularExpression(pattern: pattern, options: options)
let matches = regex.matches(in: self, options: [], range: NSRange(location: 0, length: utf16.count))
var currentIndex = startIndex
var range: Range<String.Index>
var captures: [String?] = []
var results: [SplitedResult] = []
for match in matches {
range = Range(match.range, in: self)!
if range.lowerBound > currentIndex {
results.append(SplitedResult(fragment: String(self[currentIndex ..< range.lowerBound]), isMatched: false, captures: []))
}
if match.numberOfRanges > 1 {
for i in 1 ..< match.numberOfRanges {
if let _range = Range(match.range(at: i), in: self) {
captures.append(String(self[_range]))
} else {
captures.append(nil)
}
}
}
results.append(SplitedResult(fragment: String(self[range]), isMatched: true, captures: captures))
currentIndex = range.upperBound
captures.removeAll()
}
if endIndex > currentIndex {
results.append(SplitedResult(fragment: String(self[currentIndex ..< endIndex]), isMatched: false, captures: []))
}
return results
} catch {
fatalError("正则表达式有误,请更正后再试!")
}
}
}
extension String {
// base64
func encodeBase64() -> String {
let plainData = data(using: String.Encoding.utf8)
let base64String = plainData?.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
return base64String!
}
//
func decodeBase64() -> String {
let decodedData = NSData(base64Encoded: self, options: NSData.Base64DecodingOptions(rawValue: 0))
let decodedString = NSString(data: decodedData! as Data, encoding: String.Encoding.utf8.rawValue)! as String
return decodedString
}
}
extension String {
///
public func textWidth(font: UIFont) -> CGFloat {
let str = self as NSString
let size = CGSize(width: 20000, height: 100)
let attributes = [NSAttributedString.Key.font: font]
let labelSize = str.boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil).size
return labelSize.width
}
///
///
/// - Parameters:
/// - font:
/// - size:
/// - lineBreakMode:
/// - Returns:
func size(for font: UIFont, size: CGSize, lineBreakMode: NSLineBreakMode) -> CGSize {
var attr: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: font]
if lineBreakMode != .byWordWrapping {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = lineBreakMode
attr[.paragraphStyle] = paragraphStyle
}
let rect = (self as NSString).boundingRect(with: size, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: attr, context: nil)
return rect.size
}
}
// MARK: - Extensions
extension String {
var isValidEmail: Bool {
let emailPattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailPred = NSPredicate(format: "SELF MATCHES %@", emailPattern)
return emailPred.evaluate(with: self)
}
var isValidNickname: Bool{
return self.trimmed.count >= 2
}
}
extension String {
/// Displays a number with B, M, K suffixes based on magnitude
static func displayNumber(_ num: NSNumber, scale: Int) -> String {
if num.doubleValue == 0 {
return "0"
}
let dm = NSDecimalNumber(decimal: num.decimalValue)
let handler = NSDecimalNumberHandler(
roundingMode: .down,
scale: Int16(scale),
raiseOnExactness: false,
raiseOnOverflow: false,
raiseOnUnderflow: false,
raiseOnDivideByZero: true
)
let dm_1 = NSDecimalNumber(string: "1.0")
let dm_1000 = NSDecimalNumber(string: "1000.0")
let dm_1000000 = NSDecimalNumber(string: "1000000.0")
let dm_1000000000 = NSDecimalNumber(string: "1000000000.0")
let formatter = NumberFormatter()
formatter.maximumFractionDigits = scale
formatter.numberStyle = .decimal
formatter.roundingMode = .down
formatter.locale = Locale.current // Assuming LanguagesUtils.locale() returns current locale
switch num.doubleValue {
case let value where value >= 1000000000:
let number = dm.dividing(by: dm_1000000000, withBehavior: handler)
guard let res = formatter.string(from: number) else { return "" }
return "\(res)B"
case let value where value >= 1000000:
let number = dm.dividing(by: dm_1000000, withBehavior: handler)
guard let res = formatter.string(from: number) else { return "" }
return "\(res)M"
case let value where value >= 1000:
let number = dm.dividing(by: dm_1000, withBehavior: handler)
guard let res = formatter.string(from: number) else { return "" }
return "\(res)K"
default:
let number = dm.dividing(by: dm_1, withBehavior: handler)
guard let res = formatter.string(from: number) else { return "" }
return res
}
}
/// Displays double with K rule, showing one decimal place for large numbers
static func displayDouble(_ num: Double) -> String {
if num < 1000.0 {
return displayNumber(NSNumber(value: num), scale: 0)
}
return displayNumber(NSNumber(value: num), scale: 1)
}
/// Displays integer with K rule, defaultscale is 0
static func displayInteger(_ num: Int) -> String {
displayNumber(NSNumber(value: num), scale: 0)
}
/// Displays timer from milliseconds
static func displayTimer(_ ms: Int) -> String {
let day = ms / (1000 * 60 * 60 * 24)
let hour = (ms % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
let minute = (ms % (1000 * 60 * 60)) / (1000 * 60)
let second = (ms % (1000 * 60)) / 1000
switch (day, hour, minute) {
case let (d, _, _) where d >= 1:
return String(format: "%ldd %ldh", day, hour)
case let (0, h, m) where h > 0:
return String(format: "%ldh %ldm", hour, m)
case let (0, 0, m) where m >= 0:
return String(format: "%02ld:%02ld", m, second)
default:
return String(format: "%02ld:%02ld", minute, second)
}
}
/// Formats integer with thousand separator
static func thousandString(_ num: Int) -> String {
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 0
formatter.numberStyle = .decimal
formatter.locale = Locale.current
return formatter.string(from: NSNumber(value: num)) ?? ""
}
/// Formats double with thousand separator, two decimal places
static func thousandString(float num: Double) -> String {
thousandString(float: num, maxDigits: 2)
}
/// Converts formatted string back to double
static func float(fromThousandString string: String) -> Double {
let formatter = NumberFormatter()
formatter.maximumFractionDigits = 2
formatter.numberStyle = .decimal
formatter.locale = Locale.current
return formatter.number(from: string)?.doubleValue ?? 0.0
}
/// Formats double with thousand separator and specified decimal places
static func thousandString(float num: Double, maxDigits: Int) -> String {
thousandString(float: num, maxDigits: maxDigits, roundingMode: .down)
}
/// Formats double with thousand separator, specified decimal places and rounding mode
static func thousandString(float num: Double, maxDigits: Int, roundingMode: NumberFormatter.RoundingMode) -> String {
thousandString(float: num, maxDigits: maxDigits, locale: Locale.current, roundingMode: roundingMode)
}
/// Formats double with thousand separator, specified decimal places, locale, and rounding mode
static func thousandString(float num: Double, maxDigits: Int, locale: Locale, roundingMode: NumberFormatter.RoundingMode) -> String {
let formatter = NumberFormatter()
formatter.maximumFractionDigits = maxDigits
formatter.numberStyle = .decimal
formatter.roundingMode = roundingMode
formatter.locale = locale
return formatter.string(from: NSNumber(value: num)) ?? ""
}
/// Formats number with ordinal suffix (st, nd, rd, th)
static func numberSequence(_ num: Int) -> String {
let subNum = num % 10
if num > 10 && num < 20 {
return "\(num)th"
}
switch subNum {
case 1:
return "\(num)st"
case 2:
return "\(num)nd"
case 3:
return "\(num)rd"
default:
return "\(num)th"
}
}
/// range
static func findBracketRanges(in text: String) -> [NSRange] {
// ()
let pattern = "(\\([^)]*\\))|([^]*)"
guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else {
print("Invalid regex pattern")
return []
}
let nsString = text as NSString
let range = NSRange(location: 0, length: nsString.length)
var ranges: [NSRange] = []
regex.enumerateMatches(in: text, options: [], range: range) { (match, _, _) in
if let matchRange = match?.range {
ranges.append(matchRange)
}
}
return ranges
}
/// ()
static func removeBracketContents(from text: String) -> String {
let ranges = findBracketRanges(in: text)
guard !ranges.isEmpty else { return text }
let nsString = text as NSString
let mutable = NSMutableString(string: text)
// range
for range in ranges.reversed() {
mutable.deleteCharacters(in: range)
}
return String(mutable)
}
}