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

如何从Goroutine中获取多返回值并汇总统计结果?

Go实现wc工具的Goroutine结果汇总方案

问题背景

我正在尝试用Go语言实现wc(1)工具,为了高效统计大量输入文件,我用了Goroutine做实验。当前代码能正常运行,但没法实现汇总所有Goroutine统计结果的逻辑。请问怎么把wc函数里的nl、nw、nc变量传递到main函数,等所有Goroutine执行完后进行汇总?

原代码如下:

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

func main() {
    ch := make(chan string)

    for _, arg := range os.Args[1:] {
        go wc(arg, ch)
    }
    for range os.Args[1:] {
        fmt.Println(<-ch)
    }
    // Todo: summarize results...
}

func wc(arg string, ch chan<- string) {
    nl, nw, nc := 0, 0, 0

    file, err := os.Open(arg)
    if err != nil {
        fmt.Println("Can't open file: %v", err)
    }
    defer file.Close()

    scan := bufio.NewScanner(file)
    for scan.Scan() {
        nl++

        line := scan.Text()
        words := bufio.NewScanner(strings.NewReader(line))
        words.Split(bufio.ScanWords)
        for words.Scan() {
            nw++
        }

        runes := bufio.NewReader(strings.NewReader(line))
        for {
            if _, _, err := runes.ReadRune(); err != nil {
                break
            } else {
                nc++
            }
        }
    }

    ch <- fmt.Sprintf("%8d%8d%8d", nl, nw, nc+nl)
}

解决方案

核心是用结构体传递原始统计数据,而非格式化后的字符串,这样main函数可以直接累加数值。具体修改如下:

  1. 定义统计结果结构体
    创建一个包含行数、单词数、字符数、文件名和错误信息的结构体,方便传递完整数据:
type Result struct {
    Filename string
    Lines    int
    Words    int
    Chars    int
    Err      error
}
  1. 修改wc函数的通道类型
    将通道改为传递Result类型,同时完善错误处理(比如文件打开失败时返回错误,避免panic)。

  2. 在main函数中累加汇总
    遍历通道接收每个Goroutine的结果,同时维护总行数、总单词数、总字符数的变量,最后输出汇总结果。

修改后的完整代码

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

// 定义统计结果结构体
type Result struct {
    Filename string
    Lines    int
    Words    int
    Chars    int
    Err      error
}

func main() {
    // 改为传递Result类型的通道
    ch := make(chan Result)
    files := os.Args[1:]

    // 启动所有统计Goroutine
    for _, filename := range files {
        go wc(filename, ch)
    }

    totalLines, totalWords, totalChars := 0, 0, 0

    // 接收所有结果并汇总
    for range files {
        res := <-ch
        if res.Err != nil {
            fmt.Printf("错误:无法打开文件 %s - %v\n", res.Filename, res.Err)
            continue
        }
        // 输出单个文件的统计结果
        fmt.Printf("%8d%8d%8d %s\n", res.Lines, res.Words, res.Chars, res.Filename)
        // 累加汇总数值
        totalLines += res.Lines
        totalWords += res.Words
        totalChars += res.Chars
    }

    // 如果有多个文件,输出汇总结果
    if len(files) > 1 {
        fmt.Printf("%8d%8d%8d 总览\n", totalLines, totalWords, totalChars)
    }
}

func wc(filename string, ch chan<- Result) {
    nl, nw, nc := 0, 0, 0

    file, err := os.Open(filename)
    if err != nil {
        ch <- Result{Filename: filename, Err: err}
        return
    }
    defer file.Close()

    scan := bufio.NewScanner(file)
    for scan.Scan() {
        nl++
        line := scan.Text()

        // 统计单词数
        wordScanner := bufio.NewScanner(strings.NewReader(line))
        wordScanner.Split(bufio.ScanWords)
        for wordScanner.Scan() {
            nw++
        }

        // 统计字符数(包含换行符,所以最后要加nl)
        nc += len([]rune(line))
    }

    // 加上每行的换行符,对应wc的字符统计逻辑
    totalChars := nc + nl
    ch <- Result{
        Filename: filename,
        Lines:    nl,
        Words:    nw,
        Chars:    totalChars,
        Err:      nil,
    }
}

关键说明

  • 结构体传递保留了原始数值,让汇总逻辑变得简单直接
  • 完善了错误处理,避免因单个文件打开失败导致程序崩溃
  • 字符统计逻辑和wc(1)保持一致:每行的换行符单独计入字符数
  • 当传入多个文件时,自动输出汇总的统计结果

内容的提问来源于stack exchange,提问作者Dan-Simon Myrland

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 18:20:20