bufio.Scanner 默认按行扫描,空行不会导致 Scan() 返回 false;它仅在遇到 EOF(如 Ctrl-D)或错误时停止,因此必须显式判断退出条件(如输入 "end"),无法依赖空行自动退出循环。
`bufio.scanner` 默认按行扫描,空行不会导致 `scan()` 返回 `false`;它仅在遇到 eof(如 ctrl-d)或错误时停止,因此必须显式判断退出条件(如输入 `"end"`),无法依赖空行自动退出循环。
在 Go 中,bufio.Scanner 是读取标准输入的常用工具,其行为由底层的
split function
决定。默认情况下,bufio.NewScanner(os.Stdin) 使用 bufio.ScanLines 作为分隔函数——它将输入按 \r?\n 切分,
每行(包括空行)都视为一个有效 token
,并返回该行内容(不带换行符)。关键点在于:
✅ 空行(即仅含 \n 或 \r\n 的输入)会触发 Scan() 返回 true,且 input.Text() 为空字符串 "";
❌ 空行
不会
导致 Scan() 返回 false,因此
不能用“遇空行即退出”替代显式判断
;
✅ Scan() 仅在以下情况返回 false:
输入流结束(EOF),例如在终端中按下
Ctrl+D
(Unix/macOS)或
Ctrl+Z
(Windows);
发生 I/O 错误(如断开的管道);
自定义 split 函数返回错误(默认不会)。
因此,原代码中 if input.Text() == "end" { break } 不是冗余的“额外检查”,而是
符合语义需求的必要逻辑控制
——它实现了用户主动触发的退出机制,与 EOF 的被动终止形成互补。
正确实践示例
注意事项
不要误用 len(line) == 0 作为退出条件
:这会把合法的空输入(如配置文件中的空行、交互式命令的空白确认)误判为终止信号;
EOF 是系统级信号,非用户友好
:依赖 Ctrl+D 退出对普通用户不直观,建议始终保留语义明确的指令(如 "quit"、"exit" 或 "end");
始终检查 scanner.Err()
:即使循环正常退出,也应验证扫描过程是否发生底层错误,避免静默失败。
总结:bufio.Scanner 的设计目标是
健壮地处理任意格式的文本流
,而非替代业务逻辑判断。退出条件必须由应用层根据实际需求定义——这是清晰性与可控性的最佳实践。
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
fmt.Println("Enter lines (type 'end' to exit):")
for scanner.Scan() {
line := scanner.Text()
if line == "end" {
fmt.Println("Exiting gracefully.")
break // 主动退出
}
fmt.Printf("Echo: %q\n", line)
}
// 检查扫描过程中是否发生错误(如 I/O 故障)
if err := scanner.Err(); err != nil {
fmt.Fprintf(os.Stderr, "Scanner error: %v\n", err)
}
}