bufio.scanner是交互式输入的默认选择,因其专为按行读取设计,自动处理\r\n和\n换行符,内置缓冲与边界检查,不因超长行panic(默认64kb,可调),避免readstring或readbytes导致的卡死或丢数据。

直接用 bufio.Scanner 读标准输入最稳妥,别碰 bufio.Reader.ReadString('\n') 或 bufio.Reader.ReadBytes('\n') ——换行符处理不一致、容易卡死或丢数据。
为什么 Scanner 是交互式输入的默认选择
它专为按行读取设计,自动处理 Windows/Linux/macOS 换行符(\r\n 和 \n),内置缓冲和边界检查,不会因超长行 panic(默认 64KB 限制,可调)。
- 常见错误:用
Reader.ReadString('\n')后发现 Windows 下输完按回车没反应——因为输入的是\r\n,ReadString卡在等\n,而\r还留在缓冲区 - 初始化:
scanner := bufio.NewScanner(os.Stdin) - 循环中用
scanner.Scan()判断是否读到一行,成功后用scanner.Text()取字符串(不含换行符) - 需读超长行时,提前调
scanner.Buffer(make([]byte, 0, 1 扩容(第二参数是最大 token 长度) - 读取失败时,用
scanner.Err()查错;不是io.EOF就该报错
什么时候非得用 bufio.Reader
需要精细控制字节流时:读单个字符、混合读(先读数字再读整行)、跳过空白、处理含 \r 的旧系统输入、或必须保留换行符本身。
-
reader.ReadString('\n')返回的字符串带\n,记得用strings.TrimRight(line, "\r\n")或切片line[:len(line)-1]去掉 -
reader.ReadRune()正确处理中文等 Unicode 字符;reader.ReadByte()只认 ASCII 字节 - 不能和
Scanner共用同一个os.Stdin——哪怕只创建一个Scanner,再建Reader也会读到空或错位数据 - 典型用法:
pwd, _ := reader.ReadString('\n'); pwd = strings.TrimRight(pwd, "\r\n")
bufio.Writer 写完内容没落盘?检查 Flush()
bufio.Writer 是纯内存缓冲,Write() 只是拷贝进内部 []byte,不会触发系统调用。忘记 Flush() 是最常导致“写完了但文件空”或“程序退出后内容消失”的原因。
- 每次写完关键数据(如日志行、协议帧)后,显式调用
w.Flush() - 若写入量大且对延迟不敏感,可增大缓冲区:
bufio.NewWriterSize(f, 1 - 不要混用
bufio.Writer和底层*os.File.Write()——缓冲区和文件偏移会错乱 -
defer w.Close()不等于Flush();Close()是否 flush 取决于底层类型,行为不可靠,不应依赖
混用 fmt.Scan、Scanner、Reader 会丢数据
因为 fmt.Scan 内部也偷偷用了缓冲,但没暴露出来;它读完数字后剩下一个 \n 在缓冲区里,紧接着 scanner.Scan() 或 reader.ReadString('\n') 会立刻读到空行,而不是等你输——现象就是“程序卡住不动”,其实它已经读完了,只是你没意识到。
- 常见错误链:
fmt.Scan(&n)→ 输入123↵→n=123,但\n还在 stdin 缓冲区 → 下次scanner.Scan()立刻返回true,scanner.Text()是空字符串 - 实操建议:整个程序统一用
Scanner,避免混用 - 真要解析结构化输入(如
"Alice 25"),用scanner.Text()读整行,再用strings.Fields()或fmt.Sscanf()解析,可控性更强
最易被忽略的一点:所有 Scanner 使用场景中,Scan() 返回 false 并不表示“已读完”,而是“无法再找到下一个完整分隔单元”;最后一行若无换行符,仍可通过上一次成功的 Text() 获取——但很多人只在 Scan() 为 true 时处理,结果漏掉末行。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











