Golang中如何理解Donovan和Kernighan书里的输入处理

Golang中如何理解Donovan和Kernighan书里的输入处理 大家好,Golang社区!

我是Go语言的新手,最近开始阅读Donovan和Kernighan的《Go程序设计语言》。在第一章(第1章,第3节)讨论从标准输入中查找重复行时,我遇到了以下代码:

func main() {
	counts := make(map[string]int)
	input := bufio.NewScanner(os.Stdin)  // 这一行
	for input.Scan() {  				// 还有这一行
		counts[input.Text()]++
	}
	for line, n := range counts {
		if n > 1 {
			fmt.Printf("%d\t%s\n", n, line)
		}
	}
}

作为一名具备一些C语言知识的Python开发者,我对我注释的那两行代码感到困惑。在Python和C这类语言中,使用像 input()scanf() 这样的函数来获取用户输入是很直接的,但Go的处理方式似乎有所不同。有人能帮我理解一下这几行代码在Go中是如何工作的吗?

感谢您的帮助!


更多关于Golang中如何理解Donovan和Kernighan书里的输入处理的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

这里的主要组件是 Scanner,所以请查阅文档 bufio package - bufio - Go Packages

更多关于Golang中如何理解Donovan和Kernighan书里的输入处理的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中,bufio.NewScannerScan()方法提供了一种高效处理流式输入的方式,这与C的scanf()或Python的input()的交互式输入模式有本质区别。

关键点解析:

  1. bufio.NewScanner(os.Stdin) 创建了一个带缓冲的扫描器,包装了标准输入流。缓冲机制减少了系统调用次数,提升读取性能。

  2. input.Scan() 是核心方法,它会:

    • 读取直到下一个分隔符(默认是\n
    • 返回bool表示是否成功读取
    • 将读取的内容存储在扫描器内部缓冲区
    • 遇到EOF或错误时返回false
  3. input.Text() 获取Scan()读取到的字符串内容

工作流程示例:

package main

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

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    lineNumber := 0
    
    // 持续扫描直到EOF(Ctrl+D或Ctrl+Z)
    for scanner.Scan() {
        lineNumber++
        text := scanner.Text()
        fmt.Printf("Line %d: %s\n", lineNumber, text)
    }
    
    // 检查扫描错误(非EOF错误)
    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "Reading error:", err)
    }
}

与C/Python的对比:

// Go的Scanner方式(流式)
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
    line := scanner.Text()
    // 处理每一行
}

// Python的input()方式(交互式)
while True:
    try:
        line = input()
    except EOFError:
        break

// C的scanf()方式(格式化)
char buffer[256];
while (scanf("%255[^\n]", buffer) == 1) {
    // 处理每行
}

高级用法示例:

// 自定义分隔符
scanner := bufio.NewScanner(os.Stdin)
scanner.Split(bufio.ScanWords) // 按单词扫描

// 读取大文件
file, _ := os.Open("data.txt")
defer file.Close()
scanner := bufio.NewScanner(file)
buffer := make([]byte, 0, 64*1024)
scanner.Buffer(buffer, 1024*1024) // 设置1MB缓冲区

for scanner.Scan() {
    process(scanner.Bytes()) // 直接访问字节切片,避免字符串分配
}

错误处理要点:

scanner := bufio.NewScanner(os.Stdin)
for {
    if !scanner.Scan() {
        if scanner.Err() != nil {
            // 真正的错误(如读取失败)
            log.Fatal("Scan error:", scanner.Err())
        }
        // EOF,正常结束
        break
    }
    data := scanner.Text()
    // 处理数据
}

这种设计让Go能够统一处理各种输入源(文件、网络、标准输入),同时保持内存效率和性能。Scanner会自动处理缓冲、内存分配和错误边界,比直接使用os.Stdin.Read()更安全高效。

回到顶部