String+Extension.swift 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. //
  2. // String+Extension.swift
  3. // Lanu
  4. //
  5. // Created by OneeChan on 2025/11/6.
  6. //
  7. import Foundation
  8. import CommonCrypto
  9. extension String {
  10. init(key: String) {
  11. self = LNAppConfig.shared.languageBundle.localizedString(forKey: key, value: key, table: nil)
  12. }
  13. init(key: String, _ with: any CVarArg...) {
  14. let format = LNAppConfig.shared.languageBundle.localizedString(forKey: key, value: key, table: nil)
  15. self = String(format: format, with)
  16. }
  17. }
  18. extension String {
  19. /// 计算字符串的 MD5 哈希值(小写 32 位)
  20. var md5: String {
  21. // 将字符串转换为 UTF-8 数据
  22. guard let data = self.data(using: .utf8) else {
  23. return ""
  24. }
  25. return data.md5
  26. }
  27. }
  28. extension String {
  29. func toQRCode(size: CGFloat = 200, correctionLevel: String = "H") -> UIImage? {
  30. guard let contentData = data(using: .utf8) else {
  31. Log.e("字符串编码失败(仅支持 UTF-8)")
  32. return nil
  33. }
  34. // 2. 创建 CIQRCodeGenerator 滤镜
  35. guard let qrFilter = CIFilter(name: "CIQRCodeGenerator") else {
  36. Log.e("创建二维码滤镜失败")
  37. return nil
  38. }
  39. // 3. 设置滤镜参数:内容 + 容错率
  40. qrFilter.setValue(contentData, forKey: "inputMessage")
  41. // 容错率可选值:L(7%)、M(15%)、Q(25%)、H(30%),H 容错率最高
  42. qrFilter.setValue(correctionLevel, forKey: "inputCorrectionLevel")
  43. // 4. 获取 CIImage(原始二维码是小尺寸模糊图,需缩放)
  44. guard let ciImage = qrFilter.outputImage else {
  45. Log.e("生成 CIImage 失败")
  46. return nil
  47. }
  48. // 5. 缩放 CIImage 到指定尺寸(避免模糊)
  49. let scaleX = size / ciImage.extent.width
  50. let scaleY = size / ciImage.extent.height
  51. let scaledImage = ciImage.transformed(by: CGAffineTransform(scaleX: scaleX, y: scaleY))
  52. // 6. 转为 UIImage 并返回
  53. return UIImage(ciImage: scaledImage)
  54. }
  55. }
  56. extension String {
  57. // 修复 UUID 变化后的路径
  58. var fixedFilePath: String? {
  59. let currentAppRootPath = URL.rootDir.path
  60. // 解析原始路径的「子目录+文件名」(剔除沙盒根前缀+旧UUID)
  61. let sandboxPrefix = "/var/mobile/Containers/Data/Application/"
  62. guard hasPrefix(sandboxPrefix) else {
  63. return nil
  64. }
  65. // 截取沙盒前缀后的部分(旧UUID/子目录/文件名)
  66. let pathAfterPrefix = dropFirst(sandboxPrefix.count)
  67. // 拆分出「旧UUID」和「后续路径」(以第一个"/"分割)
  68. guard let firstSlashIndex = pathAfterPrefix.firstIndex(of: "/") else {
  69. return nil
  70. }
  71. let relativePath = pathAfterPrefix[firstSlashIndex...] // 如 "/Documents/Caches/123.jpeg"
  72. // 拼接当前应用根目录 + 原子孙目录
  73. let newPath = currentAppRootPath + relativePath
  74. return newPath
  75. }
  76. }
  77. // MARK: 拼音
  78. extension String {
  79. var classificationFirstLetter: String {
  80. // 1. 空字符串直接返回"#"
  81. guard !trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
  82. return "#"
  83. }
  84. // 2. 统一预处理:转为可变字符串(支持CFStringTransform修改)
  85. let mutableString = NSMutableString(string: self) as CFMutableString
  86. // 3. 步骤1:中文转带声调拼音(对英/印尼语无影响,可安全执行)
  87. CFStringTransform(mutableString, nil, kCFStringTransformMandarinLatin, false)
  88. // 4. 步骤2:去除所有重音符号(关键:处理印尼语重音+中文拼音声调)
  89. CFStringTransform(mutableString, nil, kCFStringTransformStripDiacritics, false)
  90. // 5. 步骤3:提取处理后的字符串首字符(忽略空白字符)
  91. let processedString = mutableString as String
  92. guard let firstChar = processedString
  93. .trimmingCharacters(in: .whitespacesAndNewlines)
  94. .first else {
  95. return "#"
  96. }
  97. let firstLetter = String(firstChar).uppercased()
  98. // 6. 步骤4:判断是否为A-Z字母,统一返回结果
  99. let isValidLetter = firstLetter.range(of: "^[A-Z]$", options: .regularExpression) != nil
  100. return isValidLetter ? firstLetter : "#"
  101. }
  102. var normalizedFullString: String {
  103. guard !isEmpty else { return "" }
  104. let mutableString = NSMutableString(string: self) as CFMutableString
  105. CFStringTransform(mutableString, nil, kCFStringTransformMandarinLatin, false)
  106. CFStringTransform(mutableString, nil, kCFStringTransformStripDiacritics, false)
  107. return (mutableString as String).uppercased()
  108. }
  109. }
  110. extension String {
  111. var extractSize: CGSize {
  112. let pattern = "(\\d+)x(\\d+)"
  113. guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else {
  114. return .zero
  115. }
  116. let range = NSRange(startIndex..., in: self)
  117. guard let match = regex.firstMatch(in: self, options: [], range: range) else {
  118. return .zero
  119. }
  120. let width = if let widthRange = Range(match.range(at: 1), in: self) {
  121. Int(String(self[widthRange]))
  122. } else {
  123. 0
  124. }
  125. let height = if let heightRange = Range(match.range(at: 2), in: self) {
  126. Int(String(self[heightRange]))
  127. } else {
  128. 0
  129. }
  130. return .init(width: width!, height: height!)
  131. }
  132. }