欢迎光临
我们一直在努力

Go语言输入详解:fmt.Scan 和 bufio.NewReader 到底该怎么选?

Go语言输入详解:fmt.Scan 和 bufio.NewReader 到底该怎么选?

前言

刚开始学习 Go 的时候,输入功能看起来非常简单:

var age int
fmt.Scan(&age)

输入一个数字,程序就能正常运行。

但随着学习深入,很多问题会逐渐出现:

  • 为什么输入一句话只能读到第一个单词?
  • 为什么带空格的内容总是读取不完整?
  • 为什么有的教程使用 fmt.Scan,有的却使用 bufio.NewReader?
  • Scan、Scanln、Scanf 到底有什么区别?

很多初学者并不是不会用,而是不知道应该在什么场景下选择什么工具。

本文就从实际开发角度出发,彻底搞懂 Go 语言最常用的输入方式。


为什么会有两套输入方案

很多人第一次接触 Go 时都会疑惑:

都是读取用户输入,为什么标准库要提供两种写法?

例如:

fmt.Scan(&name)

reader := bufio.NewReader(os.Stdin)

看起来都能读取数据。

实际上:

它们解决的是两种不同的问题。


fmt.Scan 适合读取“单词”

例如:

var name string

fmt.Scan(&name)

fmt.Println(name)

输入:

张三

输出:

张三

完全正常。


但如果输入:

张三 李四 王五

结果:

张三

只有第一个单词被读取。

因为:

fmt.Scan 默认以空格、Tab、换行作为分隔符。

它真正读取的是:

一个字段(token)

而不是:

一整行文本


bufio.NewReader 适合读取整行

如果需求变成:

请输入你的个人介绍:

用户可能输入:

大家好,我来自北京,今年25岁。

这时候:

var intro string

fmt.Scan(&intro)

得到的结果只有:

大家好,我来自北京,今年25岁。

前面没有空格还能正常工作。

但如果输入:

大家好 我来自北京 今年25岁

结果变成:

大家好

后面的内容全部丢失。

因此需要:

reader := bufio.NewReader(os.Stdin)

line, _ := reader.ReadString('\\n')

一次读取整行。


fmt.Scan 和 bufio.NewReader 的本质区别

对比项fmt.Scanbufio.NewReader
读取方式 按字段读取 按行读取
空格处理 遇空格停止 保留空格
换行处理 遇换行停止 可读取整行
使用难度 简单 稍复杂
适用场景 数字、单词 长文本、句子

记住一句话:

fmt.Scan 读字段,bufio.NewReader 读整行。


bufio.NewReader 括号里到底填什么

这是很多初学者都会问的问题。

例如:

reader := bufio.NewReader(???)

这里到底写什么?

答案是:

输入源

只要实现了:

io.Reader

接口。

都可以传进去。


从键盘读取

最常见:

reader := bufio.NewReader(os.Stdin)

这里:

os.Stdin

表示标准输入。

也就是键盘。


从文件读取

file, _ := os.Open("data.txt")

reader := bufio.NewReader(file)

此时数据来自文件。


从字符串读取

reader := bufio.NewReader(
strings.NewReader("hello world"),
)

常用于测试。


为什么很多项目喜欢用 bufio

很多新手会问:

既然 Scan 更简单,为什么很多项目代码不用它?

原因主要有两个。


第一:更灵活

例如:

请输入一句话:

使用 Scan:

fmt.Scan(&msg)

遇到空格就结束。

而 Reader:

line, _ := reader.ReadString('\\n')

直接获得完整内容。


第二:更适合处理大量输入

例如:

在线判题
日志分析
文件处理

这些场景可能需要读取大量数据。

bufio 内部维护缓冲区。

能够减少系统调用次数。

性能通常更好。


Scan 的三个兄弟函数

很多人只知道:

fmt.Scan()

其实还有两个常用兄弟。


Scan

按空格或换行分隔。

var name string
var age int

fmt.Scan(&name, &age)

输入:

张三 25

输出:

name = 张三
age = 25


Scanln

读取到换行为止。

fmt.Scanln(&name, &age)

输入:

张三 25

正常。

但输入:

张三 25 程序员

可能导致读取异常。

因此:

Scanln 更适合固定数量输入。


Scanf

按照指定格式读取。

fmt.Scanf(
"姓名:%s 年龄:%d",
&name,
&age,
)

必须输入:

姓名:张三 年龄:25

格式不匹配就会失败。


两个最容易踩的坑

忘记写 &

错误写法:

var age int

fmt.Scan(age)

报错。

原因:

Scan需要变量地址

正确:

fmt.Scan(&age)


Reader 读取后忘记去掉换行符

例如:

line, _ := reader.ReadString('\\n')

此时:

line

实际内容:

hello\\n

包含换行。

因此通常需要:

line = strings.TrimSpace(line)

或者:

line = strings.TrimRight(
line,
"\\r\\n",
)


一张图看懂如何选择

需要读取数字?


fmt.Scan

需要读取单词?


fmt.Scan

需要读取一句话?


bufio.NewReader

需要读取文件?


bufio.NewReader

需要读取大量数据?


bufio.NewReader

需要固定格式解析?


fmt.Scanf


常见面试题

fmt.Scan 和 bufio.NewReader 的区别是什么?

回答:

fmt.Scan 按空格和换行分隔读取字段。

bufio.NewReader 可以按行读取,
能够保留空格内容。

实际开发中:

简单输入通常使用 Scan。

需要读取整行文本、
文件内容或者大量数据时,
更常使用 bufio.NewReader。


总结

很多初学者会把:

fmt.Scan

和:

bufio.NewReader

理解成两种功能重复的输入方式。

实际上并不是。

它们分别解决不同问题:

  • Scan 负责读取字段
  • Reader 负责读取整行

因此记住一句话即可:

读数字、读单词,用 fmt.Scan;读句子、读文件、读大量数据,用 bufio.NewReader。

理解这一点之后,以后再遇到 Go 输入相关问题,基本就知道该选择哪种方案了。

赞(0)
未经允许不得转载:171主机测评 » Go语言输入详解:fmt.Scan 和 bufio.NewReader 到底该怎么选?
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址