You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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])
}

修复说明

  1. 修正统计逻辑:使用strings.Count统计每个元音在字符串中的出现次数,累加得到总次数,匹配预期统计目标。
  2. 修复变量捕获:在循环内创建局部变量currentOffset捕获当前偏移量,并作为参数传递给goroutine,确保每个goroutine处理正确的文件块。
  3. 优化分块读取:直接按字节块读取,不再丢弃边界内容,避免数据丢失;同时处理最后一块的大小,避免超出文件范围。
  4. 使用int64计数器:避免大文件统计时出现整数溢出问题。

运行修复后的代码,统计结果会与预期一致。

内容的提问来源于stack exchange,提问作者Curious

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 19:20:30