简单分析Go语言的scan输入 | 青训营笔记

254 阅读3分钟

这是我参与「第五届青训营 」笔记创作活动的第一天,在本日的学习中,主要简单了解了GO语言的基础语法与一些基础库函数。其中,最为基础也最为有用的莫过于输入。下面将结合课程样例简单分析GO语言中使用fmt包的输入方式。

1、fmt.Scan()

类似C++中的 cin ,参数给定多个逗号分隔的变量地址,会从标准输入流中依次读入这些变量(会根据变量类型自动推断输入的值),且读入完成前一直处于等待状态,直到读入完成。

例子:

var a int
var b string
var c bool
var d float64
fmt.Scan(&a, &b, &c, &d)
fmt.Println(a, b, c, d)

运行结果:

不在一行也会等待全部输入完成:

123 awa
true 

3.14
123 awa true 3.14

根据类型自动匹配相应输入:

0x10 10.01W 0 1e9
16 10.01W false 1e+09

2、fmt.Scanln()

与Scan类似,不同的是该函数最多只读取标准输入流的一行,且对输入值不足时后续值不做处理。简单来说,该函数会在输入完所有变量或者遇到回车时结束。

例子:

var a int
var b string
var c bool
var d float64
fmt.Scanln(&a, &b, &c, &d)
fmt.Println(a, b, c, d)
fmt.Scanln(&a, &b, &c, &d)
fmt.Println(a, b, c, d)

运行结果:

输入不足时后续值不做处理,保持原值:

123 awa true 3.14
123 awa true 3.14
1 a
1 a true 3.14

3、fmt.Scanf()

基础功能与 C 的scanf函数基本一样,不同的是该函数也是非阻塞的,和fmt.Scanln一样不会等待所有变量输入完成,未输入的变量值不做处理。函数根据第一项参数的格式化字符串输入后续变量,格式化字符串中的常值将被读走不做处理。

例子:

var a int
var b string	
var c bool
var d float64
fmt.Scanf("myinput: %d%s%t%f\n", &a, &b, &c, &d)
fmt.Println(a, b, c, d)
fmt.Scanf("%d%s%t%f", &a, &b, &c, &d)
fmt.Println(a, b, c, d)

运行结果:

myinput: 1 test true 3.14
1 test true 3.14
0 end
0 end true 3.14

容易遇到的问题:

在课程的猜数游戏中,如果简单的使用fmt.Scanf("%d",&guess)进行循环输入,会发现输入一个数字换行后,出现下面的情况:

Please input your guess
50
You guess is 50
Your guess is bigger than the secret number. Please try again
You guess is 0
Your guess is smaller than the secret number. Please try again

简单分析一下可以得知,输入的50作为了一个Scanf的输入,该函数已经结束,而后输入的回车换行也被识别为了下一次循环的结束标识,所以导致局部变量guess保持了默认值0,同时又进行了一次无用猜测。

解决方法:

Scanf改为Scan或者将格式化字符串改为"%d\n"读取多余回车。

4、其他输入方式

显然fmt包中的scan家族是面向标准输入流的,而在许多其他时候,我们还需要面向文件流、网络写入流等输入。这时候就可以参考课程中的方式,使用bufio包中的Reader,可以根据给定输入流构建Reader,然后就能利用io自带的一系列Read函数或bufio中的一系列Read函数进行输入。