os.stdin 是标准输入流,scan 等是其读取工具;fmt.scan/sscanf 跳过空白并以空白分割,不适用于含空格字符串;安全读一行应使用 bufio.newreader(os.stdin).readstring('\n') 并处理换行符。

Go 里读标准输入,os.Stdin 和 Scan 不是同一层概念
很多人误以为 os.Stdin 和 Scan 是并列的“输入方式”,其实 os.Stdin 是一个 *os.File 类型的变量,代表标准输入流;而 Scan(比如 fmt.Scan、fmt.Scanner.Scan)是读取它的工具之一。真正要对比的,是不同读取方法对 os.Stdin 的使用方式。
fmt.Scan 和 fmt.Scanf 默认跳过空白符,不适合读含空格的字符串
它们内部调用 os.Stdin,但会自动跳过前导空白(空格、制表符、换行),且以空白为分隔符切割输入。这在读单个数字或单词时方便,但容易出错:
- 输入
"hello world",fmt.Scan(&s)只读到"hello","world"留在缓冲区,下次Scan会直接取走它 - 如果紧接着读整数,可能因残留换行导致
Scan阻塞或失败 -
fmt.Scanf("%s", &s)行为同Scan,不是按行读,别被格式字符串误导
想安全读一行?用 bufio.NewReader(os.Stdin).ReadString('\n')
这是最常用、最可控的按行读法,明确处理换行符:
- 返回的字符串包含末尾的
'\n',通常需用strings.TrimSpace或strings.TrimSuffix(s, "\n")去掉 - 若用户只按 Ctrl+D(EOF),
ReadString返回io.EOF错误,需显式检查 - 比
Scanln更可靠:后者遇到 EOF 会 panic(Go 1.22+ 已修复,但旧版本仍常见)
reader := bufio.NewReader(os.Stdin)
line, err := reader.ReadString('\n')
if err != nil {
log.Fatal(err) // 或判断 err == io.EOF
}
line = strings.TrimSpace(line)
fmt.Scanln 要求输入严格以换行结尾,否则阻塞
它和 Scan 类似,但要求最后一个 token 后必须紧跟换行符(不能是空格或 EOF)。这个限制非常隐蔽:
立即学习“go语言免费学习笔记(深入)”;
- 终端中输入
"123 "(末尾空格)再回车 →Scanln卡住,等下一个非空白输入 - 输入
"123"后直接 Ctrl+D → 返回err != nil,且n == 0,不填充变量 - 它不适用于交互式命令行的健壮输入,仅适合已知格式的简单测试
真正需要“读一行且解析多个值”时,应先用 ReadString 读整行,再用 fmt.Sscanf 解析,控制权完全在你手上。










