如何从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函数可以直接累加数值。具体修改如下:
- 定义统计结果结构体
创建一个包含行数、单词数、字符数、文件名和错误信息的结构体,方便传递完整数据:
type Result struct { Filename string Lines int Words int Chars int Err error }
修改wc函数的通道类型
将通道改为传递Result类型,同时完善错误处理(比如文件打开失败时返回错误,避免panic)。在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
相关产品推荐
相关产品推荐

