如何让SwiftUI中的字符串匹配函数支持多关键词非连续匹配?
解决字符串多单词非连续匹配问题
这个问题很常见——localizedStandardContains(_:) 本质是检查完整子串是否连续存在,所以当目标文本里的两个单词中间插入了其他内容时,整个搜索字符串自然匹配不上。咱们来调整逻辑,让它能适配两种场景:
问题根源
localizedStandardContains 会严格匹配输入的整个字符串,比如你搜索的是 "like pineapple",它只会找连续出现这两个词的子串。但 fullTextLong 里这两个词中间有 "green",所以返回 false 是这个方法的预期行为,但不符合你的需求。
解决方案:拆分单词逐一匹配
我们可以把搜索文本拆成独立单词,然后验证每个单词都存在于目标文本中,同时保留 localizedStandardContains 的本地化匹配特性(比如忽略大小写、特殊字符的本地化处理)。
修改后的完整代码
struct ContainsFuncView: View { let fullTextShort = "I like pineapple" let fullTextLong = "I like green pineapple" let containsText = "like pineapple" var body: some View { VStack { contains(type: "Short") contains(type: "Long") } } func contains(type: String) -> Text { let targetText = type == "Short" ? fullTextShort : fullTextLong let containsInFullText = matchesAllSearchWords(containsText, in: targetText) return Text("\(containsInFullText ? "Contains" : "Doesn't contain") in fullText\(type)") .foregroundColor(containsInFullText ? .green : .red) } // 辅助函数:检查所有搜索单词是否都存在于目标文本中 private func matchesAllSearchWords(_ searchText: String, in targetText: String) -> Bool { // 拆分搜索文本为单词数组,过滤掉空字符串(处理多空格情况) let searchWords = searchText.components(separatedBy: .whitespacesAndNewlines) .filter { !$0.isEmpty } // 如果搜索文本为空,默认返回true(可根据需求调整) guard !searchWords.isEmpty else { return true } // 逐一检查每个单词是否在目标文本中存在 return searchWords.allSatisfy { targetText.localizedStandardContains($0) } } }
进阶需求:按顺序匹配单词
如果你需要确保单词按指定顺序出现(比如不能是 "pineapple like"),可以改用正则表达式实现,允许单词中间存在任意内容:
private func matchesOrderedWords(_ searchText: String, in targetText: String) -> Bool { let searchWords = searchText.components(separatedBy: .whitespacesAndNewlines) .filter { !$0.isEmpty } .map { NSRegularExpression.escapedPattern(for: $0) } guard !searchWords.isEmpty else { return true } // 构建正则:单词之间允许任意内容(.*),忽略大小写 let pattern = searchWords.joined(separator: ".*") let regex = try! NSRegularExpression(pattern: pattern, options: .caseInsensitive) let range = NSRange(targetText.startIndex..., in: targetText) return regex.firstMatch(in: targetText, options: [], range: range) != nil }
只要把 contains 函数里的 matchesAllSearchWords 换成这个方法,就能确保 "like" 一定出现在 "pineapple" 之前,同时兼容中间有其他内容的场景。
内容的提问来源于stack exchange,提问作者cluelessCoder
相关产品推荐
相关产品推荐

