Android语音识别:连字符、复合词等单词匹配问题的解决方案
语音识别与原文单词匹配解决方案
问题场景与需求
在Android中实现语音识别功能:将固定文本显示在TextView中,把文本按空格分割为单词列表;通过onPartialResult获取逐步返回的语音输入(比如先返回[I],再返回[I am]),每次取语音输入的最后一个单词,与文本中的单词按顺序匹配,匹配成功后用Spannable给对应单词着色,同时维护全局索引跟踪匹配进度。要求Spannable文本必须与TextView原文完全一致,不能修改原文。
遇到的匹配问题
- 文本中带连字符的单词(如
here-there),语音识别会拆成here、there两个独立单词,无法匹配; - 文本中的复合词(如
carefree),语音识别拆成care、free,难以匹配; - 文本中的所有格单词(如
day's),语音识别返回days,当前用替换处理,需要更优方案; - 有没有不用修改语音识别结果或原文的匹配算法/工具库?手动处理各类问题效率低还影响识别速度。
当前使用代码
private void applyColor(String param){ ArrayList<String> params = paraToArray(param); // from string only get last word String newWord = params.get(params.size()-1); // get current word of paragraph by using global index tracking String currentWord_FromParagraph = paragraphWords.get(indexHistory); // handle 's words if(currentWord_FromParagraph.contains("'s")){ String rootWord = currentWord_FromParagraph.replaceAll("'s\\b", ""); newWord = rootWord+"'s"; } // index of word on paragraph int start = paragraph.indexOf(newWord, indexHistoryChar); int end = newWord.length(); // match if(start != -1 && newWord.equalsIgnoreCase(currentWord_FromParagraph) ){ // spannable contains paragraph and its global spannableString.setSpan(new ForegroundColorSpan(Color.GREEN), start, start+end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); // global index for array of words of paragraph. after matching ignore current index so we look for next word indexHistory++; // similar as above but it is for paragraph character index indexHistoryChar = start+end; spannableString.setSpan(new ForegroundColorSpan(Color.WHITE), startNext, endNext, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } txtv_paragraph.setText(spannableString); }
解决方案
1. 连字符/复合词:换字符序列渐进匹配
别再死磕单个单词的匹配了,改成跟踪语音输入的完整字符序列和原文的匹配进度:
- 全局维护一个
currentMatchPos,记录当前在原文中匹配到的字符位置; - 每次拿到
onPartialResult的完整文本(别只取最后一个单词),把语音文本和原文从currentMatchPos开始的部分都做标准化:去掉空格、连字符,统一转小写; - 找语音文本在标准化后原文片段里的最长匹配前缀,然后对着原文里对应的实际字符范围着色;
- 最后把
currentMatchPos更新到匹配结束的位置。
举个例子:原文是here-there,语音先返回here,标准化后都是here,匹配到原文0-3位的here,着色;接着语音返回here there,标准化后是herethere,原文从第4位开始标准化是there,匹配到原文5-9位的there,直接把整个here-there着色就行。
2. 所有格单词:用规则化模糊匹配
别直接改newWord,而是匹配时做规则化对比:
- 把原文单词和语音单词都转成标准化形式:比如
day's转成days,语音返回的days也转成days,对比标准化后的内容; - 规则可以扩展:比如去掉所有格的
's、统一大小写、忽略特殊符号等; - 只要标准化后内容一致,就直接给原文里的
day's着色,完全不用改原文或语音结果。
3. 工具库:用编辑距离做模糊匹配
试试用编辑距离(Levenshtein Distance)计算语音内容和原文单词的相似度,设个阈值(比如相似度≥90%)就算匹配成功,不用改任何内容:
- 自己实现编辑距离计算函数就行,代码量不大;或者用轻量的工具类,比如Apache Commons Lang里的
StringUtils.getLevenshteinDistance(注意Android端引入依赖的体积); - 遇到复合词拆分的情况,把连续几个语音单词拼接起来,再和原文单词算相似度:比如语音返回
care free,拼成carefree后和原文的carefree算,完全匹配就着色整个单词。
4. 优化匹配逻辑:别卡单词索引
当前用indexHistory按单词索引硬匹配,很容易因为语音拆分卡壳。改成基于字符位置的弹性匹配:
- 每次匹配从
currentMatchPos开始,在原文里找和当前语音输入最匹配的字符范围,不是固定找第indexHistory个单词; - 比如语音先返回
care,匹配到carefree的前4个字符,着色;接着返回care free,直接匹配到整个carefree,着色整个单词,同时更新currentMatchPos到单词结束的位置。
内容的提问来源于stack exchange,提问作者CrackerKSR
相关产品推荐
相关产品推荐

