如何区分字符串中相同片段,解决同姓名用户提及替换错误问题
解决方案
最优方案:基于位置偏移替换
这是社交产品提及功能的标准实现,完全不会出现同名混淆问题,仅需要你在Mention类中补充记录每个提及在原始文本中的起止位置即可:
fun String.transformMentionForBackend(mentions: List<Mention>): String { if (mentions.isEmpty()) return this val resultBuilder = StringBuilder(this) // 按起始位置倒序排列,从字符串末尾往开头替换,避免替换后长度变化影响前面的位置偏移 mentions.sortedByDescending { it.start } .forEach { mention -> resultBuilder.replace( mention.start, mention.end, getMentionTag(mention.id, mention.name) ) } return resultBuilder.toString() } private fun getMentionTag(id: Int, name: String): String { return "<mention id=\"$id\">@$name</mention>" } // Mention类补充位置字段示例 data class Mention( val id: Int, val name: String, val start: Int, // 提及在原始字符串中的起始索引 val end: Int // 提及在原始字符串中的结束索引 )
该方案完全不依赖名字匹配,只要位置记录准确,不管有多少个同名用户都能准确替换成对应标签。
过渡方案:按出现顺序匹配替换
如果你暂时无法拿到提及的位置信息,且能保证传入的mentions列表顺序和文本中提及出现的顺序完全对应,可以用正则跳过已替换的标签内容逐次替换:
fun String.transformMentionForBackend(mentions: List<Mention>): String { var result = this if (mentions.isEmpty()) return result for (mention in mentions) { // 正则仅匹配不在mention标签内的目标名字,避免匹配到已替换的标签内容 val nameRegex = Regex("(?<!<mention[^>]*>)${Regex.escape(mention.name)}(?!</mention>)") result = nameRegex.replaceFirst(result, getMentionTag(mention.id, mention.name)) } return result } private fun getMentionTag(id: Int, name: String): String { return "<mention id=\"$id\">@$name</mention>" }
该方案有两个使用限制:
- 必须保证
mentions列表顺序和文本中名字出现的顺序完全一致 - 用
Regex.escape()转义名字,避免名字中的特殊正则字符导致匹配异常
注意:原有代码中的distinctBy{ Pair(it.id, it.name) }会过滤掉同一个用户的多次提及,如果你的业务允许同一条内容里多次@同一个用户,需要删掉这段去重逻辑。
内容的提问来源于stack exchange,提问作者G. Rann
相关产品推荐
相关产品推荐

