You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何替换AttributedString中的字符以保留Markdown解析后的换行

解决Swift中用.full解析Markdown后换行丢失的问题

使用.full语法解析Markdown时,虽然能支持标题(.inlineOnly和.inlineOnlyPreservingWhitespace会忽略标题),但解析后所有换行都会被移除。尝试先将换行替换为虚拟Unicode字符♥,解析完成后再换回换行,但直接操作AttributedString的字符时出现崩溃或无法正确替换的问题。

方案一:借助NSMutableAttributedString处理替换

Swift的AttributedString是值类型,直接循环修改字符索引容易因索引失效导致崩溃。可以先将其转换为NSMutableAttributedString,利用Foundation的字符串替换API完成操作,再转换回AttributedString:

func getAttributedText(text: String) -> AttributedString {
    let textWithReplacements = text.replacingOccurrences(of: "\n", with: "\n♥")
    
    guard let attributedText = try? AttributedString(
        markdown: textWithReplacements,
        options: .init(
            allowsExtendedAttributes: true,
            interpretedSyntax: .full,
            failurePolicy: .returnPartiallyParsedIfPossible
        ),
        baseURL: nil
    ) else {
        return AttributedString(stringLiteral: text)
    }
    
    // 转换为可变NSAttributedString
    let nsMutableAttr = NSMutableAttributedString(attributedText)
    let fullRange = NSRange(location: 0, length: nsMutableAttr.length)
    
    // 批量替换所有♥为换行
    nsMutableAttr.mutableString.replaceOccurrences(of: "♥", with: "\n", options: [], range: fullRange)
    
    // 转换回Swift AttributedString
    guard let result = try? AttributedString(nsMutableAttr, including: \.uiKit) else {
        return attributedText
    }
    
    return result
}

方案二:纯Swift原生从后往前替换

如果不想依赖Foundation,可以从后往前遍历AttributedString的字符,这样修改时不会影响未处理的索引,避免崩溃:

func getAttributedText(text: String) -> AttributedString {
    let textWithReplacements = text.replacingOccurrences(of: "\n", with: "\n♥")
    
    guard var attributedText = try? AttributedString(
        markdown: textWithReplacements,
        options: .init(
            allowsExtendedAttributes: true,
            interpretedSyntax: .full,
            failurePolicy: .returnPartiallyParsedIfPossible
        ),
        baseURL: nil
    ) else {
        return AttributedString(stringLiteral: text)
    }
    
    // 从后往前遍历,防止索引失效
    var currentIndex = attributedText.endIndex
    while currentIndex > attributedText.startIndex {
        currentIndex = attributedText.index(before: currentIndex)
        if attributedText[currentIndex] == "♥" {
            // 替换当前♥为换行
            attributedText.replaceSubrange(currentIndex...currentIndex, with: "\n")
        }
    }
    
    return attributedText
}

原代码崩溃原因

你之前的循环操作直接使用了原attributedText的字符索引,而AttributedString是值类型,修改output后,原索引已经和新字符串不匹配,继续使用会导致索引越界,引发崩溃。从后往前遍历或借助NSMutableAttributedString都能避免这个问题。

内容的提问来源于stack exchange,提问作者Programmer54

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 22:04:51