这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天。今天试着写了下课后作业,结果遇到了个 BUG,在这里记录下。
猜数游戏:初始时程序随机生成一个 0 到 100 之间的整数,玩家每次输入一个猜测的整数,如果没有猜中,程序返回玩家猜测的数是偏大还是偏小,接下来玩家继续猜测,如此循环直到猜中为止。
下面是我写的简化版的猜数游戏,使用 fmt.Scanf 读取输入。
package main
import (
"fmt"
"math/rand"
)
func main() {
maxNum := 100
secretNum := rand.Intn(maxNum)
for {
var inputNum int
fmt.Print("Your guess number: ")
fmt.Scanf("%d", &inputNum)
if inputNum < secretNum {
fmt.Println("Your guess is smaller than the secret number. Please try again!")
} else if inputNum > secretNum {
fmt.Println("Your guess is bigger than the secret number. Please try again!")
} else {
fmt.Printf("The secret number is %d\n", secretNum)
break
}
}
}
在运行时,发现输出并不符合预期:
明明只输入一个数字,却输出了两行 "try again"。
因为之前学 C 语言的时候遇到过很多次类似的问题,所以很快确定不是代码逻辑问题,而应该是换行符的问题,于是写了下面的代码验证一下:
func main() {
var a int
_, err := fmt.Scanf("%d", &a)
fmt.Println(err)
fmt.Printf("input: %d\n", a)
_, err = fmt.Scanf("%d", &a)
fmt.Println(err)
fmt.Printf("input: %d\n", a)
}
同样是输入一个数,输出了两条语句:
第一次 fmt.Scanf 没的返回错误信息,而第二次则返回了 unexpected newline.
那么很明显了,应该是第一个 fmt.Scanf 读取时将换行符保留在了 stdin 中,后面又被第二个 fmt.Scanf 读取了(因为读取时出错了,所以 a 的值不变)。
其实这个 fmt.Scanf 函数介绍已经解答了这个问题,来看看 fmt.Scanf 的函数介绍:
划重点:"Newline in the input must match newlines in the format."
也就是说,在使用 fmt.Scanf 读取标准输入中包含的换行符时需要用 '\n' 与之匹配。这一点与 C 语言中的 scanf 是不一样的,C 语言中的 scanf 遇到换行符时会视为空白符,直接忽略,并不需要 '\n' 与之匹配。
所以在这里应该给 Scanf 补上 '\n':
func main() {
var a int
_, err := fmt.Scanf("%d\n", &a)
fmt.Println(err)
fmt.Printf("input: %d\n", a)
_, err = fmt.Scanf("%d\n", &a)
fmt.Println(err)
fmt.Printf("input: %d\n", a)
}
嗯,这下输出正常了。
像这种题目在之前学 C 语言的时候已经写了很多遍,本来没打算写的,没想到随手写了下还是能发现问题,所以学写代码还是得多尝试多动手。