
本文介绍在算法竞赛等场景中,如何优雅、健壮地从标准输入读取指定数量的数值或字符串(如首行给出个数 n,随后 n 行为数据),并对比原始写法,提供更符合 go 语言惯用法的优化方案。
在算法题输入处理中,常见模式是:第一行给出数据个数 n,接下来 n 行依次为具体值(整数、浮点数或字符串)。原始代码使用 for { ... break } 循环配合手动计数,结构松散且未处理输入错误,可读性与鲁棒性均不足。
更推荐的做法是将循环逻辑收敛到 for 语句头部,利用 Go 的多重赋值和条件判断能力,使意图更清晰。同时,必须检查 fmt.Scanf 的返回错误——这是生产级和竞赛级代码的关键实践,避免因格式错误或 EOF 导致静默失败或 panic。
以下为优化后的标准模板(以读取 n 个整数为例):
package main
import "fmt"
func main() {
var nums []int
var count int
var err error
// 读取总数,支持换行符自动跳过(%d\n 隐式吸收)
for _, err = fmt.Scanf("%d\n", &count); err == nil && count > 0; count-- {
var x int
_, err = fmt.Scanf("%d\n", &x)
if err != nil {
break // 输入异常时立即退出,避免追加无效值
}
nums = append(nums, x)
}
if err != nil && err != fmt.EOF {
panic(fmt.Sprintf("input error: %v", err))
}
// 此时 nums 已包含全部有效输入
fmt.Println(nums) // 示例输出:[78 42 99]
}✅ 关键改进点说明:
- 循环控制集中化:for 头部完成初始化、条件判断与迭代,逻辑一目了然;
- 作用域最小化:x 仅在单次迭代内声明,避免变量污染;
- 错误即刻响应:每次 Scanf 后检查 err,异常时跳出循环,防止继续解析损坏数据;
- EOF 容忍处理:对 fmt.EOF 不 panic(常出现在测试用例末尾),仅对真实解析错误 panic;
- 格式健壮性:%d\n 可安全跳过行末空白(包括 \r\n 在 Windows 下),兼容多数 OJ 环境。
? 扩展支持其他类型:
- 读取 float64:将 %d 替换为 %f,变量类型改为 float64;
- 读取字符串(每行一个):使用 %s(不带空格)或 %v + strings.TrimSpace,或更稳妥地用 bufio.Scanner(适合含空格的字符串):
import "bufio"
// ...
scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
n, _ := strconv.Atoi(scanner.Text())
for i := 0; i < n; i++ {
scanner.Scan()
s := strings.TrimSpace(scanner.Text())
// 处理字符串 s
}? 注意事项:
- fmt.Scanf 对输入格式敏感,若数据行含多余空格或非数字字符,会返回错误;
- 竞赛中若输入规模大(如 n > 10⁵),建议改用 bufio.Scanner + strconv 组合,性能更高且可控;
- 始终优先验证 err,切勿忽略——这是调试超时/ Wrong Answer 的首要排查点。
掌握这种简洁、健壮、符合 Go 惯用法的输入模式,能显著提升算法题编码效率与稳定性。










