直接用 bufio.scanner 读标准输入最稳妥,它自动处理各系统换行符、内置缓冲与边界检查;避免用 readstring('\n') 或 readbytes('\n'),易因换行符差异卡死或丢数据。

直接用 bufio.Scanner 读标准输入最稳妥,别碰 bufio.Reader.ReadString('\n') 或 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 扩容 - 读取失败时,用
scanner.Err()查错,不是io.EOF就该报错
Reader.ReadString 要手动清理 \r,且必须检查 err == io.EOF
它只认字面 '\n',遇到 \r\n 会把 \r 当作行末内容一起返回,导致字符串末尾多出 \r。很多用户输完回车发现字符串带 \r,再 trim 又可能误删业务数据。
使用场景有限:仅当你明确控制输入源(比如读自己生成的 Unix 风格文件),或必须逐字符/逐分隔符解析时才考虑。
实操建议:
- 每次读完立即用
strings.TrimRight(line, "\r\n")清理,不能只 trim\r -
err为io.EOF是正常结束,不是错误;其他err(如io.ErrUnexpectedEOF)才需处理 - 别用
for line, err := r.ReadString('\n'); err == nil; line, err = r.ReadString('\n')这种写法——err第一次就可能是io.EOF,循环根本进不去
Scanln 和 fmt.Scanf 不适合混合读取或粘包场景
它们底层也用 bufio,但行为更“激进”:自动跳过空白、按空格切词、无法控制缓冲大小。一旦输入格式和预期不符(比如用户多打了空格、漏了数字),后续读取全乱。
典型问题:先用 fmt.Scanln(&name) 读名字,再用 scanner.Scan() 读下一行,结果第二行读到的是空字符串——因为 Scanln 没吃掉结尾的换行符,Scanner 直接把它当空行返回了。
实操建议:
- 整个程序统一用
Scanner,避免混用 - 真要解析结构化输入(如 “Alice 25”),用
scanner.Text()读整行,再用strings.Fields()或fmt.Sscanf()解析,可控性更强 -
fmt.Scanf的格式串里别写空格以外的固定字符(如"%s,%d"),CSV 类输入优先走字符串拆分
真正难的不是选哪个 API,而是意识到:标准输入不是文件流,它的“行”由终端行为决定,而终端对 \r 的处理永远比代码想象得更随意。多一个 TrimRight、少一次混用、早一步设缓冲上限,比背熟所有方法名管用得多。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











