GO语言–关于scanf、scan、scanln函数使用过程中的一些坑
前段时间一直弄不清scanf、scan、scanln三个函数在使用场景和使用细节上的差别,这里我整理了一下
关于scanf,首先我遇到过这种情况,在使用了多个scanf的时候,不像c语言那样,可以输入多行,摁多次回车
package mainimport "fmt"//假如我们要输入一个人的年龄和名字
func main() {
var name stringvar age int8fmt.Scanf("%s", &name)fmt.Scanf("%d", &age)//fmt.Scanf("%s %d", &name, &age) 对于scanf,这句话等价于上面两句话 fmt.Println(name, " ", age)
}
这两种写法都一样,如果我要把名字和年龄分两行输入是不行的,运行结果就像这样:
第一种输入方法(一次性输入):
第二种输入方法(把名字和年龄分两次输入):
可以看到第二种情况age没有被赋值
这是为什么呢?
我们看看scanf的函数原型
其实scanf两个返回值n和err,n是按指定格式成功输入数据的个数,err是读取过程中遇到的错误,如果没有错误,err的值就为nil
package mainimport "fmt"//假如我们要输入一个人的年龄和名字
func main() {
var name stringvar age int8n, err := fmt.Scanf("%s %d", &name, &age) //用n,err分别接受scanf扫描成功的个数和错误返回值if err == nil{
//如果没有错误就输出name和age的值fmt.Println(name, " ", age)}else{
fmt.Println("读取成功",n, "个","错误:",err)}}
我们用刚刚第二种没有成功的输入方法试试,看是什么错误
可以看到,我们只成功输入了bob这一个数据,有一个错误叫unexpected newline,这个错误其实就是我们输入的回车,因为scanf函数遇到scanf就结束,从缓冲区依次读取以空格分开的数据;对我们这个程序而言,首先按%s读入了bob,然后再按%d读取下一个数据(回车),但是回车键不是十进制整形数据,它按%d怎么可能读得进去呢,所以就出现了只成功读取一个数据,报错为 “没有意料到的新行”
既然把scanf函数搞懂了,再来看看scan和scanln又是怎么回事
首先,它们的函数原型
跟scanf差不多,都是有两个返回值,一个是读取成功个数,另一个是错误值
但是如果像刚刚那样用会发生什么
package mainimport "fmt"//假如我们要输入一个人的年龄和名字
func main() {
var name stringvar age int8n, err := fmt.Scan(&name, &age) //用n,err分别接受scanf扫描成功的个数和错误返回值/*n, err := fmt.Scan(&name)n, err = fmt.Scan(&age)*/ //对于scan这种写法也等价于上面那种写法if err == nil{
//如果没有错误就输出name和age的值fmt.Println("输出:", name, " ", age)}else{
fmt.Println("读取成功",n, "个","错误:",err)}
}
对于scan,我们是可以多个数据多行输入的
对于scanln,又有些不同了
package mainimport "fmt"//假如我们要输入一个人的年龄和名字
func main() {
var name stringvar age int8n, err := fmt.Scanln(&name, &age) //这种写法的话必须把name和age一行输入,因为scanln是以回车为标志结束n, err := fmt.Scanln(&name) //这样写就可以分两行输入name和agen, err = fmt.Scanln(&age)if err == nil{
//如果没有错误就输出name和age的值fmt.Println("输出:", name, " ", age)}else{
fmt.Println("读取成功",n, "个","错误:",err)}
}
其实scanln再换行的时候会把缓冲区的回车也收走,但是scan和scanf不会,所以就导致了scanf不能分多行输入数据。但是scan却可以,它虽然没有收走缓冲区的回车符,但是不会把回车符读进去,遇到回车它会继续读取下一个数据,而scanf会按照我们给的格式(如%d去读取数据),但是肯定读不进去的,所以就读取失败了
总结一下
scanf:按照给定的格式依次读取数据(包括非法数据),不能换行输入(如果要换行需要在前面加一个scanln吸收掉回车符,就像c语言中的getchar)
scan:比scanf高级,依次读取数据,遇到回车会忽略,可以换行输入(如果要先用了scan输入,再用scanf输入的话,需要在中间加一个scanln)
scanln:类似scan,但是遇到换行(回车)立马结束输入,如果要换行输入必须用多个scanln
下面有几个例子:
package mainimport "fmt"//假如我们要输入一个人的年龄和名字
func main() {
var name stringvar age int8fmt.Scan(&name)// 把Scan换成Scanln就可以了n, err := fmt.Scanf("%d", &age)//原因:scan没有把第一行输入结束后的回车收走,导致scanf按%d的格式去读取回车符,那肯定读取失败啊//而scanln会把第一行输入结束的回车符读走,scanf会按%的格式去读取我们后面输入的数据if err == nil{
fmt.Println("输出:", name, " ", age)}else{
fmt.Println("读取成功",n, "个","错误:",err)}
}
package mainimport "fmt"//假如我们要输入一个人的年龄和名字
func main() {
var name stringvar age int8fmt.Scan(&name)fmt.Scanln()fmt.Scanf("%d", &age)}
}
}
```go
package mainimport "fmt"//假如我们要输入一个人的年龄和名字
func main() {var name stringvar age int8fmt.Scan(&name)fmt.Scanln()fmt.Scanf("%d", &age)}