
fmt.Scanln 会截断首空格后的输入,导致 strings.Split 实际处理的字符串过短而引发索引越界;应改用 bufio.Scanner 完整读取一行,再安全分割。
`fmt.scanln` 会截断首空格后的输入,导致 `strings.split` 实际处理的字符串过短而引发索引越界;应改用 `bufio.scanner` 完整读取一行,再安全分割。
在 Go 中,fmt.Scanln 的行为常被误解:它按空白符(空格、制表符、换行符)分隔输入,并仅读取第一个字段,随后丢弃该行剩余内容。因此,当用户输入 "2 3" 时,fmt.Scanln(&in) 仅将 "2" 赋值给 in,wh = strings.Split(in, " ") 得到的是 []string{"2"} —— 长度仅为 1,访问 wh[1] 必然触发 panic: index out of range。
✅ 正确做法是使用 bufio.Scanner 完整读取一整行(包括空格),再进行分割:
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
var seedCost float64
var lawnNo int
var in string
var area float64
var wh []string
scanner := bufio.NewScanner(os.Stdin)
// 读取 seedCost
scanner.Scan()
seedCost, _ = strconv.ParseFloat(scanner.Text(), 64)
// 读取 lawnNo
scanner.Scan()
lawnNo, _ = strconv.Atoi(scanner.Text())
lawnCost := 0.00
for i := 0; i < lawnNo; i++ {
scanner.Scan()
in = scanner.Text() // ✅ 完整读取整行,如 "2 3"
wh = strings.Split(in, " ") // → []string{"2", "3"}
// ⚠️ 安全检查:避免索引越界
if len(wh) < 2 {
fmt.Fprintf(os.Stderr, "警告:输入格式错误,期望至少两个数字,得到 %v\n", wh)
continue
}
w, err1 := strconv.ParseFloat(wh[0], 64)
h, err2 := strconv.ParseFloat(wh[1], 64)
if err1 != nil || err2 != nil {
fmt.Fprintf(os.Stderr, "解析错误:w=%s, h=%s\n", wh[0], wh[1])
continue
}
area = w * h
lawnCost += area * seedCost
}
ans := strconv.FormatFloat(lawnCost, 'E', 8, 64)
fmt.Println(ans)
}? 关键要点总结:
-
fmt.Scanln≠ “读一行”,而是“读下一个空白分隔的字段”,不适用于含空格的原始行输入; -
bufio.Scanner是标准、高效、推荐的逐行读取方式,自动处理换行、缓冲和 UTF-8; - 始终对
strings.Split结果做长度校验(len(wh) >= N),避免运行时 panic; - 使用
strconv解析时务必检查错误,而非忽略_(生产代码中应妥善处理错误)。
通过切换输入方式并增加基础校验,即可稳定支持 "2 3"、"10.5 7.2" 等多字段输入场景。

















