
在使用 fmt.Scanf() 从标准输入读取数据时,如果用户输入了无效的数据,例如期望输入整数却输入了字符串,fmt.Scanf() 会返回一个错误,并且无效的输入会残留在标准输入缓冲区中。在循环中重复调用 fmt.Scanf() 时,由于缓冲区中仍然存在无效数据,程序会不断地读取到这些数据,导致无限循环。
解决这个问题的一种有效方法是使用 bufio 包来创建一个带缓冲的读取器,并在每次读取失败后清除缓冲区。bufio.NewReader 函数可以创建一个新的带缓冲的读取器,它从指定的 io.Reader 读取数据,这里我们可以使用 os.Stdin 作为输入源。bufio.Reader 提供了 ReadString('\n') 方法,可以读取直到遇到换行符为止的所有数据,并将这些数据从缓冲区中清除。
以下是一个示例代码,演示了如何使用 bufio 包来清除 fmt.Scanf() 后的标准输入缓冲区:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
stdin := bufio.NewReader(os.Stdin)
fmt.Println("Please enter an integer: ")
var userI int
for {
_, err := fmt.Fscan(stdin, &userI)
if err == nil {
break
}
stdin.ReadString('\n') // 清除缓冲区
fmt.Println("Sorry, invalid input. Please enter an integer: ")
}
fmt.Println(userI)
}代码解释:
立即学习“go语言免费学习笔记(深入)”;
- stdin := bufio.NewReader(os.Stdin): 创建一个新的带缓冲的读取器,它从标准输入 os.Stdin 读取数据。
- _, err := fmt.Fscan(stdin, &userI): 使用 fmt.Fscan 从带缓冲的读取器 stdin 读取一个整数,并将其存储到变量 userI 中。 fmt.Fscan 类似于 fmt.Scanf,但它从 io.Reader 读取数据,而不是直接从标准输入读取。
- stdin.ReadString('\n'): 如果读取失败(err != nil),则调用 stdin.ReadString('\n') 来读取并丢弃缓冲区中剩余的数据,直到遇到换行符为止。 这有效地清除了缓冲区中的无效输入。
运行示例:
如果用户输入有效的整数,程序会正常读取并输出:
Please enter an integer: 3 3
如果用户输入无效的字符串,程序会提示用户重新输入,并清除缓冲区,避免无限循环:
Please enter an integer: what? Sorry, invalid input. Please enter an integer: 5 5
注意事项:
- 使用 bufio 包可以有效地处理标准输入缓冲区中的残留数据,避免无限循环。
- ReadString('\n') 方法会一直读取直到遇到换行符,如果用户输入的数据中没有换行符,程序可能会一直阻塞。 在实际应用中,可以设置读取超时来避免这种情况。
- 除了 ReadString('\n') 方法,bufio.Reader 还提供了其他方法来读取数据,例如 ReadBytes 和 ReadLine,可以根据实际需求选择合适的方法。
总结:
本文介绍了如何使用 bufio 包来清除 fmt.Scanf() 后的标准输入缓冲区,避免由于无效输入导致的无限循环。通过使用 bufio.NewReader 和 ReadString('\n') 方法,我们可以有效地处理标准输入,并提供更友好的用户体验。 掌握这种技巧对于编写交互式命令行程序至关重要。










