你说你想迭代NSRange
匹配字符串,以便您可以将粗体属性应用于相关子字符串。
在 Swift 5.7 及更高版本中,您可以使用新的Regex https://developer.apple.com/documentation/swift/regex:
string.ranges(of: /\d+/)
.map { NSRange($0, in: string) }
.forEach {
attributedString.setAttributes(attributes, range: $0)
}
或者,如果您发现传统的正则表达式太神秘,您可以导入RegexBuilder https://developer.apple.com/documentation/regexbuilder,并且您可以使用新的正则表达式 DSL:
string.ranges(of: Regex { OneOrMore(.digit) })
.map { NSRange($0, in: string) }
.forEach {
attributedString.setAttributes(attributes, range: $0)
}
现在,这种映射模式Range<String.Index>
to a NSRange
仅当您确实需要时才需要NSRange
,现在很少需要。例如,如果您只想打印子范围,您可以这样做:
string.ranges(of: /\d+/)
.forEach { print(string[$0]) }
或者,如果您需要更新可变的AttributedString
在寻找原始范围的基础上String
,你可以映射到Range<AttributedString.Index>
:
string.ranges(of: /\d+/)
.compactMap { Range($0, in: attributedString) }
.forEach { attributedString[$0].foregroundColor = .blue }
但关键的一点是我们现在可以使用原生Regex
文字包围着/
字符,如上所示。
在 5.7 之前的 Swift 版本中,人们会使用NSRegularExpression https://developer.apple.com/documentation/foundation/nsregularexpression/. E.g.:
let range = NSRange(location: 0, length: string.count)
try! NSRegularExpression(pattern: "\\d+").enumerateMatches(in: string, range: range) { result, _, _ in
guard let range = result?.range else { return }
attributedString.setAttributes(attributes, range: range)
}
因为正则表达式反斜杠位于字符串内部,所以必须用另一个反斜杠对其进行转义(这就是其中有两个反斜杠的原因)。或者您可以使用扩展字符串分隔符,替换"\\d+"
with #"\d+"#
.
就我个人而言,在 Swift 5.7 之前,我发现有一个返回 Swift 范围数组的方法很有用,即[Range<String.Index>]
:
extension StringProtocol {
func ranges<T: StringProtocol>(of string: T, options: String.CompareOptions = []) -> [Range<Index>] {
var ranges: [Range<Index>] = []
var start: Index = startIndex
while let range = range(of: string, options: options, range: start ..< endIndex) {
ranges.append(range)
if !range.isEmpty {
start = range.upperBound // if not empty, resume search at upper bound
} else if range.lowerBound < endIndex {
start = index(after: range.lowerBound) // if empty and not at end, resume search at next character
} else {
break // if empty and at end, then quit
}
}
return ranges
}
}
然后你可以像这样使用它:
let string = "Hello world, there are 09 continents and 195 countries."
let ranges = string.ranges(of: "[0-9]+", options: .regularExpression)
然后你就可以map
the Range
to NSRange
。回到最初的示例,如果您想在某些属性字符串中将这些数字设为粗体:
string.ranges(of: "[0-9]+", options: .regularExpression)
.map { NSRange($0, in: string) }
.forEach { attributedString.setAttributes(boldAttributes, range: $0) }
资源:
- Swift 5.7 and later:
- WWDC 2022 视频认识 Swift 正则表达式 https://developer.apple.com/videos/play/wwdc2022/110357/
- WWDC 2022 视频Swift Regex:超越基础知识 https://developer.apple.com/videos/play/wwdc2022/110358/
- 使用 Swift 进行黑客攻击:常用表达 https://Regular%20expressions%20Available%20from%20Swift%205.7
- Swift before 5.7:
- 使用 Swift 进行黑客攻击:如何在 Swift 中使用正则表达式 https://www.hackingwithswift.com/articles/108/how-to-use-regular-expressions-in-swift
- NSHipster:Swift 中的正则表达式 https://nshipster.com/swift-regular-expressions/