字符匹配字符串
标题可能表达的不太准确,需求是这样的:我们对用户语音通过TensorFlowLite识别,识别结果是26字母中的字母,然后要得到语音句子中哪些单词说对了哪些说错了,比如句子是“I am a ball”识别结果是“i a m b l l”。这样的识别结果应该是I AM BALL是对的 A是错误的。
#pragma mark ==========AVAudioRecorderDelegate========== - ( void ) audioRecorderDidFinishRecording:(AVAudioRecorder *)avrecorder successfully:( BOOL )flag{ NSLog (@ "录音完毕。开始识别" ); NSLog (@ "当前句子%@" , self .sentenceModel.sentence_en); //去掉标点符号 NSString * res = [[TFLiteManager shareManager] recognizeAudioWithPath:[ self getRecorderPath] text: self .sentenceModel.sentence_en]; NSLog (@ "识别结果:%@" ,res); [ self setupRecognizeRes:res]; } - ( void )setupRecognizeRes:( NSString *)res{ self .recognizeCorrectCount = 0; self .totalPhomeCount = 0; [ self .recognizeRes removeAllObjects]; //识别结果通过空格分割后的集合 self .recognizeRes = [ NSMutableArray arrayWithArray:[res componentsSeparatedByString:@ " " ]]; //lowerString小写文本、为了查询字母在原文本中的位置 NSString * sentence_en = [ self .sentenceModel.sentence_en mutableCopy]; self .upperString = [[ NSMutableAttributedString alloc]initWithString:sentence_en]; self .lowerString = [[ NSMutableAttributedString alloc]initWithString:[sentence_en lowercaseString]]; //目标文本 NSString * orgin_text = [ self .sentenceModel.sentence_en mutableCopy]; //删除特殊字符便于后面处理 orgin_text = [orgin_text stringByReplacingOccurrencesOfString:@ "\"" withString:@ "" ]; orgin_text = [orgin_text stringByReplacingOccurrencesOfString:@ "." withString:@ "" ]; orgin_text = [orgin_text stringByReplacingOccurrencesOfString:@ "," withString:@ "" ]; orgin_text = [orgin_text stringByReplacingOccurrencesOfString:@ "!" withString:@ "" ]; orgin_text = [orgin_text stringByReplacingOccurrencesOfString:@ "?" withString:@ "" ]; //全部变小写 orgin_text = [orgin_text lowercaseString]; //先将目标文本根据空格分割 NSArray * originWords = [orgin_text componentsSeparatedByString:@ " " ]; self .totalPhomeCount = originWords.count; for ( NSString * sub in originWords) { [ self isCorrectSub:sub]; } //识别结果处理完毕 [ self updateDownbooks]; BOOL allRecorded = [ self checkBookPagesisAllRecorded]; NSLog (@ "是否全录音:%d" ,allRecorded); if (allRecorded) { NSLog (@ "全录完,上传音频,获取最终得分" ); self .bookModel.recorderIndex = [AppSingle sharedAppSingle].record_id; self .bookModel.bookScore = [ self getBookScore]; self .bookModel.time = [YCUtils getTimestampString]; [[AppSingle sharedAppSingle].recordedBooks addObject: self .bookModel]; [AppSingle sharedAppSingle].record_id ++ ; [SaveCachesFile saveDataList:[AppSingle sharedAppSingle].recordedBooks fileName:RecordedList]; } } //计算所有句子的平均分 - ( float )getBookScore{ NSInteger sentenceCount = 0; NSInteger sentenceScore = 0; for ( NSInteger i = 0 ;i < self .bookModel.pages.count ; i++) { PageModel * page = self .bookModel.pages[i]; for ( NSInteger j = 0; j < page.sentences.count ; j ++) { SentenceModel * sentence = page.sentences[j]; sentenceScore += sentence.score; sentenceCount ++ ; } } float res = ( float )sentenceScore/( float )sentenceCount; NSLog (@ "最终得分:%f" ,res); return res; } //更新本地数据 - ( void )updateDownbooks{ float score = ( float ) self .recognizeCorrectCount/( float ) self .totalPhomeCount; self .currentSpeakCell.scoreView.score = score; //再找到对应的page for (PageModel * page in self .bookModel.pages) { if ([page.page_id isEqualToString: self .sentenceModel.page_id]) { //再找到对应的句子 for (SentenceModel * sentence in page.sentences) { if ([sentence.sentence_id isEqualToString: self .sentenceModel.sentence_id]) { sentence.attributedString = ( NSMutableAttributedString *) self .currentBookCell.lb.attributedText; sentence.score = score; sentence.hasRecord = YES ; } } } } } /** 检查是否本书内句子是否全部录音 全部已录音返回YES; */ - ( BOOL )checkBookPagesisAllRecorded{ for (PageModel * page in self .bookModel.pages) { for (SentenceModel * sentence in page.sentences) { if (sentence.hasRecord == NO ) { return NO ; } } } return YES ; } - ( void )isCorrectSub:( NSString *)sub{ self .right = 0; self .wrong = 0; //sub是单词,分割为字母 for ( NSInteger i = 0; i < sub.length; i++) { NSString * phomes = [sub substringWithRange: NSMakeRange (i, 1)]; NSInteger index = [ self getIndexWithTemp:phomes]; if (index == -1) { //没有找到对应的,开始进行下一次的遍历 //字母错误 self .wrong ++; } else { //字母正确 self .right ++; } //正确与否都降遍历后的删除掉,进行后续遍历 [ self .recognizeRes removeObjectsInRange: NSMakeRange (0, index+1)]; //单词遍历完成 if (i == sub.length - 1) { //查找单词在文本中的位置 NSMutableArray *locationArr = [ self calculateSubStringCount: self .lowerString.string str:sub]; //筛选正确位置 NSInteger loc = [ self getCorrectLoc:locationArr sub:sub]; if (loc == -1) { NSLog (@ "loction error" ); return ; } float score = ( float ) self .right/( float )sub.length; if (score >= 0.5) { self .recognizeCorrectCount ++; //变绿色 [ self .upperString addAttribute: NSForegroundColorAttributeName value:greenColor range: NSMakeRange (loc, sub.length)]; } else { //变红色 [ self .upperString addAttribute: NSForegroundColorAttributeName value:redColor range: NSMakeRange (loc, sub.length)]; } self .currentBookCell.lb.attributedText = self .upperString; } } } - ( NSInteger )getCorrectLoc:( NSMutableArray *)locationArr sub:( NSString *)sub{ //筛选正确的location 比如this is 中匹配 is。mom o for ( NSInteger i = 0; i < locationArr.count; i ++) { NSInteger loc = [locationArr[i] integerValue]; if (loc == 0) { return 0; } else { //前一位必须是空格 NSMutableAttributedString * fontLocString =( NSMutableAttributedString *)[ self .lowerString attributedSubstringFromRange: NSMakeRange (loc - 1, 1)]; //字符匹配 NSMutableAttributedString * other_sub = ( NSMutableAttributedString *)[ self .lowerString attributedSubstringFromRange: NSMakeRange (loc , sub.length)]; //后一位不能是字母 NSMutableAttributedString * afterLocString = ( NSMutableAttributedString *)[ self .lowerString attributedSubstringFromRange: NSMakeRange (loc + sub.length, 1)]; //同时满足这三个情况才是正确的loc if ([@ " " isEqualToString:fontLocString.string] && [sub isEqualToString:other_sub.string] && ![ self inputIsPhome:afterLocString.string]) { return loc; } } } return -1; } //判断字符是否是26个字母中的一个 - ( BOOL )inputIsPhome:( NSString *)input{ for ( NSString * sub in self .phomes) { if ([input isEqualToString:sub]) { return YES ; } } return NO ; } /** 查找子字符串在父字符串中的所有位置 @param content 父字符串 @param tab 子字符串 @return 返回位置数组 */ - ( NSMutableArray *)calculateSubStringCount:( NSString *)content str:( NSString *)tab { int location = 0; NSMutableArray *locationArr = [ NSMutableArray new ]; NSRange range = [content rangeOfString:tab]; if (range.location == NSNotFound ){ return locationArr; } //声明一个临时字符串,记录截取之后的字符串 NSString * subStr = content; while (range.location != NSNotFound ) { if (location == 0) { location += range.location; } else { location += range.location + tab.length; } //记录位置 NSNumber *number = [ NSNumber numberWithUnsignedInteger:location]; [locationArr addObject:number]; //每次记录之后,把找到的字串截取掉 subStr = [subStr substringFromIndex:range.location + range.length]; // NSLog(@"subStr %@",subStr); range = [subStr rangeOfString:tab]; // NSLog(@"rang %@",NSStringFromRange(range)); } return locationArr; } - ( NSInteger )getIndexWithTemp:( NSString *)temp{ NSInteger index; for ( NSInteger i = 0; i < self .recognizeRes.count; i ++) { NSString *index_str = self .recognizeRes[i]; if ([index_str isEqualToString:temp]) { index = i; return index; } } //如果都没有匹配到返回-1 index = - 1; return index; } |
核心代码是isCorrectSub。注释挺多的,有疑问可以评论。
那个获取location的最优解应该有更好的方法。有更好的方法希望不吝赐教。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?