Swift中能否用NSRegularExpression实现JS式函数替换字符串?
当然可以实现!虽然Swift标准库的String没有像JavaScript那样直接支持带回调函数的replace方法,但借助NSRegularExpression,我们完全能复刻你想要的功能——甚至可以封装得更顺手。
核心思路
JavaScript里的replace回调,本质是遍历每一个正则匹配结果,用自定义逻辑处理捕获组后生成替换内容。在Swift中,我们可以用NSRegularExpression的enumerateMatches(in:options:range:)方法遍历所有匹配,然后手动处理替换逻辑,同时注意维护字符串长度变化带来的位置偏移。
针对你的需求的具体实现
先看直接实现的代码,我修正了正则让它正确捕获中间的文本内容,确保替换结果符合预期:
let originalText = "*Hello there*" // 正则捕获:第一个*、中间的文本、第二个* let regexPattern = "(\\*)(.*?)(\\*)" do { let regex = try NSRegularExpression(pattern: regexPattern, options: .dotMatchesLineSeparators) let fullTextRange = NSRange(originalText.startIndex..., in: originalText) var modifiedText = originalText var offset = 0 // 记录替换导致的字符串长度偏移,避免后续匹配位置错误 regex.enumerateMatches(in: originalText, options: [], range: fullTextRange) { match, _, _ in guard let match = match else { return } // 提取各个捕获组的内容 guard let contentRange = Range(match.range(at: 2), in: originalText) else { return } let highlightedContent = String(originalText[contentRange]) // 生成替换后的内容(对应你JS里的回调返回值) let replacement = "<i>" + highlightedContent + "</i>" // 计算当前在修改后字符串中的匹配范围(因为之前的替换会改变长度) let currentMatchRange = NSRange(location: match.range.location + offset, length: match.range.length) // 执行替换 modifiedText = (modifiedText as NSString).replacingCharacters(in: currentMatchRange, with: replacement) // 更新偏移量:替换后的长度 - 原匹配片段的长度 offset += replacement.count - match.range.length } print(modifiedText) // 输出: <i>Hello there</i> } catch { print("正则表达式初始化失败: \(error.localizedDescription)") }
封装成可复用的扩展
如果你的Markdown解析器需要多次用到这类替换逻辑,可以把它封装成String的扩展,用法会更接近JavaScript的replace:
extension String { /// 类似JS的replace方法,支持正则匹配+自定义回调生成替换内容 func regexReplace(pattern: String, options: NSRegularExpression.Options = [], using callback: (NSTextCheckingResult, String) -> String) throws -> String { let regex = try NSRegularExpression(pattern: pattern, options: options) let fullRange = NSRange(startIndex..., in: self) var result = self var offset = 0 regex.enumerateMatches(in: self, options: [], range: fullRange) { match, _, _ in guard let match = match else { return } // 调用回调生成替换内容 let replacement = callback(match, self) // 修正当前匹配的位置 let currentRange = NSRange(location: match.range.location + offset, length: match.range.length) result = (result as NSString).replacingCharacters(in: currentRange, with: replacement) // 更新偏移量 offset += replacement.count - match.range.length } return result } }
使用这个扩展的示例:
do { let input = "*Hello there*" let output = try input.regexReplace(pattern: "(\\*)(.*?)(\\*)") { match, original in guard let contentRange = Range(match.range(at: 2), in: original) else { return (original as NSString).substring(with: match.range) // 匹配失败则返回原内容 } let content = String(original[contentRange]) return "<i>" + content + "</i>" } print(output) // <i>Hello there</i> } catch { print(error.localizedDescription) }
注意事项
- NSRange与Swift Range的转换:
NSRegularExpression基于NSRange工作,而Swift的String用的是Range<String.Index>,所以需要用Range(_:in:)方法来转换,避免索引错误。 - 偏移量维护:每次替换都会改变字符串的长度,如果不更新
offset,后续的匹配位置会因为字符串长度变化而错位,这是实现中最容易踩坑的点。 - 正则匹配选项:如果你的Markdown文本包含换行,可以加上
.dotMatchesLineSeparators选项,让正则的.匹配换行符。
内容的提问来源于stack exchange,提问作者linguine
相关产品推荐
相关产品推荐

