
fmt.Scanf 用于按格式解析标准输入,需区分输入解析(如 %d 读整数)与输出格式化(如 %#X 输出十六进制),不能在扫描时直接用 %#X 读取;支持多参数及带分隔符/固定文本的模式匹配。
go 中 `fmt.scanf` 用于按格式解析标准输入,需区分输入解析(如 `%d` 读整数)与输出格式化(如 `%#x` 输出十六进制),不能在扫描时直接用 `%#x` 读取;支持多参数及带分隔符/固定文本的模式匹配。
fmt.Scanf 是 Go 标准库 fmt 包中用于从标准输入(stdin)按指定格式读取并解析数据的函数。它与 fmt.Scan 和 fmt.Scanln 不同,核心特点是支持格式化字符串(format string),可精确匹配输入中的文本结构,适用于解析结构化输入(如含分隔符、固定前缀或混合类型的数据)。
✅ 正确理解输入 vs 输出格式
一个常见误区是认为 Scanf("%#X", &e) 能直接将输入的十进制数 "10" 解析为十六进制值 0xA —— 这是错误的。%#X 在 Scanf 中是输入格式说明符,表示“期望用户输入一个以 0X 或 0x 开头的十六进制字面量”(例如输入 0XA 或 0x10),而非将十进制输入自动转为十六进制显示。
若用户输入的是纯十进制数字(如 10),应使用 %d 解析:
var e int
fmt.Scanf("%d", &e) // 成功读入 10
fmt.Printf("%#X\n", e) // 输出:0XA(此处 %#X 是输出格式,非输入格式)
⚠️ 注意:
Scanf的格式动词仅控制如何从输入流中提取并转换数据,不改变变量存储值的本质。int类型变量始终以二进制补码形式存储,%#X等仅影响其打印时的字符串表示。
✅ 多参数输入:空格分隔与自定义分隔符
Scanf 支持多个变量,格式字符串中各动词依次对应参数。空白字符(空格、制表符、换行)在格式串中会匹配任意空白序列:
var num int
var name string
fmt.Scanf("%d %s", &num, &name) // 输入:"42 hello" → num=42, name="hello"
更强大的是,Scanf 可嵌入字面文本进行精确匹配,实现“模板式”解析:
var a, b int
fmt.Scanf("Value: %d and %d", &a, &b)
// 输入必须为:Value: 100 and 200 → a=100, b=200
// 若输入 "100 200" 则失败(缺少 "Value: " 和 "and")
✅ Scanf vs Scan:何时选择?
| 特性 | fmt.Scan |
fmt.Scanf |
|---|---|---|
| 语法 | Scan(&a, &b, &c) |
Scanf("pattern %d %s", &a, &b) |
| 分隔符 | 自动跳过任意空白(空格/换行/制表符) | 格式串中空白匹配任意空白;非空白字符必须严格匹配 |
| 灵活性 | 简单、健壮,适合交互式逐项输入 | 强大但易出错,适合解析已知结构的输入(如日志行、配置片段) |
| 错误处理 | 遇到不匹配立即返回错误 | 同上,但错误信息更难调试(因依赖格式串) |
示例对比:
// 使用 Scan:输入三行数字即可
var x, y, z int
fmt.Scan(&x, &y, &z) // 输入:<br>10<br>20<br>30 → 成功
// 使用 Scanf:要求单行且带固定文本
fmt.Scanf("Scores: %d %d %d", &x, &y, &z) // 输入:"Scores: 10 20 30" → 成功
? 实用建议与注意事项
-
始终检查错误:
Scanf返回(n int, err error),建议显式处理:if _, err := fmt.Scanf("%d", &e); err != nil { log.Fatal("Invalid input:", err) } -
避免过度依赖
Scanf:对于简单输入,优先用Scan或bufio.Scanner+ 字符串处理,更清晰可控。 -
格式动词需严格匹配类型:
%d对应*int,%s对应*string,类型不匹配会导致未定义行为。 -
输入缓冲区:
Scanf会读取直到匹配完成,多余字符留在缓冲区,可能影响后续读取(可用bufio.NewReader(os.Stdin)精细控制)。
掌握 Scanf 的关键在于明确:它是输入解析器,不是输出格式化工具。正确分离“如何读”(Scanf 格式)和“如何印”(Printf 格式),才能写出健壮、可维护的命令行程序。










