当前位置: 代码迷 >> 综合 >> GO语言输入函数--关于scanf、scan、scanln函数使用过程中的一些坑
  详细解决方案

GO语言输入函数--关于scanf、scan、scanln函数使用过程中的一些坑

热度:33   发布时间:2023-11-24 22:34:02.0

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)}
  相关解决方案