使用Go并发统计文本文件元音数量结果不符求助
问题分析与修复
你的代码存在核心逻辑错误与goroutine执行异常,导致统计结果远低于预期,以下是具体问题和修复方案:
1. 统计逻辑错误:错把「含元音的单词数」当「元音总次数」
当前代码用strings.Contains判断单词是否包含元音,每匹配一次计数器仅加1,这是在统计包含该元音的单词数量,而非文本中元音的总出现次数。比如单词"Banana"包含3个A,你的代码只会给A的计数器加1,但预期是加3,这是结果差距的主要原因。
2. Goroutine变量捕获问题:分块偏移量混乱
循环中启动goroutine时直接引用current变量,由于goroutine的执行时机不确定,多个goroutine会共享同一个current的引用,导致实际读取的偏移量不是循环时的预期值,出现重复读取或漏读的情况。
3. 次要问题:分块边界处理丢失数据
read函数中,当offset !=0时,会调用ReadBytes(' ')跳过当前位置到下一个空格的内容,这部分文本会被直接丢弃,导致这部分的元音没有被统计。
修复后的代码
package main import ( "bufio" "fmt" "io" "os" "strings" "sync" ) const mb = 1024 * 1024 func main() { var aCount, eCount, iCount, oCount, uCount int64 // 用int64避免大文件统计溢出 wg := &sync.WaitGroup{} channel := make(chan string, 100) // 增加缓冲区避免goroutine阻塞 done := make(chan bool, 1) // 统计goroutine:改为统计每个元音的出现次数 go func() { for s := range channel { upperStr := strings.ToUpper(s) aCount += int64(strings.Count(upperStr, "A")) eCount += int64(strings.Count(upperStr, "E")) iCount += int64(strings.Count(upperStr, "I")) oCount += int64(strings.Count(upperStr, "O")) uCount += int64(strings.Count(upperStr, "U")) } done <- true }() var current int64 limit := int64(0.5 * mb) // 明确转换为int64类型 fileInfo, err := os.Stat("interview.txt") if err != nil { panic(err) } totalSize := fileInfo.Size() // 修复变量捕获问题:用局部变量保存当前偏移量,确保每个goroutine拿到正确值 for i := 0; current < totalSize; i++ { wg.Add(1) currentOffset := current currentChunkLimit := limit // 最后一块可能不足limit,调整大小避免超出文件范围 if currentOffset+currentChunkLimit > totalSize { currentChunkLimit = totalSize - currentOffset } go func(offset, chunkLimit int64) { defer wg.Done() read(offset, chunkLimit, "interview.txt", channel) fmt.Printf("%d thread has been completed\n", i) }(currentOffset, currentChunkLimit) current += currentChunkLimit } wg.Wait() close(channel) <-done close(done) fmt.Println("A - ", aCount) fmt.Println("E - ", eCount) fmt.Println("I - ", iCount) fmt.Println("O - ", oCount) fmt.Println("U - ", uCount) } // 修改read函数:直接按字节块读取,不丢弃边界内容,避免数据丢失 func read(offset int64, limit int64, fileName string, channel chan string) { file, err := os.Open(fileName) if err != nil { panic(err) } defer file.Close() _, err = file.Seek(offset, io.SeekStart) if err != nil { panic(err) } reader := bufio.NewReader(file) buffer := make([]byte, limit) n, err := reader.Read(buffer) if err != nil && err != io.EOF { panic(err) } // 将读取到的有效字节转为字符串发送到通道 channel <- string(buffer[:n]) }
修复说明
- 修正统计逻辑:使用
strings.Count统计每个元音在字符串中的出现次数,累加得到总次数,匹配预期统计目标。 - 修复变量捕获:在循环内创建局部变量
currentOffset捕获当前偏移量,并作为参数传递给goroutine,确保每个goroutine处理正确的文件块。 - 优化分块读取:直接按字节块读取,不再丢弃边界内容,避免数据丢失;同时处理最后一块的大小,避免超出文件范围。
- 使用int64计数器:避免大文件统计时出现整数溢出问题。
运行修复后的代码,统计结果会与预期一致。
内容的提问来源于stack exchange,提问作者Curious
相关产品推荐
相关产品推荐

