Swift自定义拼写检查算法求助:无需UITextChecker,支持字符容错
不依赖UITextChecker的拼写检查方案(适配数字混淆场景)
我之前也碰到过类似的需求——不用系统自带的UITextChecker,还要能识别像ce3t这类带数字的拼写错误,把它转换成正确的cat。下面给你一套落地性很强的实现思路和代码:
核心逻辑拆解
这类问题本质是处理输入混淆(数字转字母) + 拼写相似度匹配,分两步走就搞定:
- 先把字符串里的数字转换成可能对应的字母(比如3常被误输成a或e,0对应o这类常见混淆),生成所有可能的纯字母候选串;
- 用编辑距离(Levenshtein Distance)计算候选串和字典单词的相似度,挑出最匹配的结果。
具体代码实现
1. 先搞个数字-字母混淆映射表
先定义日常输入里最常见的数字转字母的对应关系,你可以根据自己的场景调整:
let numberToLetters: [Character: [Character]] = [ "0": ["o"], "1": ["i", "l"], "2": ["z"], "3": ["e", "a"], // 对应你例子里的3→a "4": ["a"], "5": ["s"], "6": ["b", "g"], "7": ["t"], "8": ["b"], "9": ["g", "q"] ]
2. 生成所有可能的纯字母候选串
写个递归函数,把带数字的输入转换成所有可能的纯字母组合:
func generatePossibleStrings(from input: String) -> [String] { guard let firstChar = input.first else { return [""] } let rest = String(input.dropFirst()) let possibleChars: [Character] if let letters = numberToLetters[firstChar] { possibleChars = letters } else { possibleChars = [firstChar] } let restStrings = generatePossibleStrings(from: rest) return possibleChars.flatMap { char in restStrings.map { String(char) + $0 } } }
3. 实现编辑距离计算
编辑距离用来衡量两个字符串的相似度,数值越小越像,这是拼写检查的核心:
func levenshteinDistance(_ s1: String, _ s2: String) -> Int { let rows = s1.count + 1 let cols = s2.count + 1 var distanceMatrix = Array(repeating: Array(repeating: 0, count: cols), count: rows) // 初始化边界 for i in 0..<rows { distanceMatrix[i][0] = i } for j in 0..<cols { distanceMatrix[0][j] = j } // 填充矩阵 for i in 1..<rows { for j in 1..<cols { let s1Char = s1[s1.index(s1.startIndex, offsetBy: i-1)] let s2Char = s2[s2.index(s2.startIndex, offsetBy: j-1)] let cost = s1Char == s2Char ? 0 : 1 distanceMatrix[i][j] = min( distanceMatrix[i-1][j] + 1, // 删除操作 distanceMatrix[i][j-1] + 1, // 插入操作 distanceMatrix[i-1][j-1] + cost // 替换操作 ) } } return distanceMatrix[rows-1][cols-1] }
4. 匹配字典里的最佳单词
准备一个常用单词字典(你可以导入本地词库或者用现成的),然后遍历候选串找到最匹配的单词:
func bestMatchingWord(for input: String, dictionary: [String]) -> String? { let lowercasedInput = input.lowercased() let possibleStrings = generatePossibleStrings(from: lowercasedInput) var bestWord: String? var minDistance = Int.max for candidate in possibleStrings { for word in dictionary { let distance = levenshteinDistance(candidate, word.lowercased()) if distance < minDistance { minDistance = distance bestWord = word } } } return bestWord }
测试一下你的例子
// 随便整个测试用的字典 let testDictionary = ["cat", "dog", "car", "cake", "cent"] if let result = bestMatchingWord(for: "ce3t", dictionary: testDictionary) { print("匹配到的单词:\(result)") // 输出 cat }
优化小技巧
- 字典提速:如果字典很大,建议用前缀树(Trie)来代替全量遍历,能大幅提升查找速度;
- 动态映射:如果是特定场景(比如OCR识别、手机键盘输入),可以调整数字-字母的映射表,加入更多场景化的混淆对应;
- 阈值过滤:可以设置一个编辑距离阈值,比如超过2就忽略,避免匹配到完全不相关的单词。
内容的提问来源于stack exchange,提问作者Alexyesiam
相关产品推荐
相关产品推荐

