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

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

前段时间一直弄不清scanf、scan、scanln三个函数在使用场景和使用细节上的差别,这里我整理了一下

关于scanf,首先我遇到过这种情况,在使用了多个scanf的时候,不像c语言那样,可以输入多行,摁多次回车

package main

import "fmt"

//假如我们要输入一个人的年龄和名字
func main()  {
    
    
	var name string
	var age int8
	fmt.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 main

import "fmt"

//假如我们要输入一个人的年龄和名字
func main()  {
    
    
	var name string
	var age int8
    n, 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 main

import "fmt"

//假如我们要输入一个人的年龄和名字
func main()  {
    
    
	var name string
	var age int8
	n, 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 main

import "fmt"

//假如我们要输入一个人的年龄和名字
func main()  {
    
    
	var name string
	var age int8
	n, err := fmt.Scanln(&name, &age)  //这种写法的话必须把name和age一行输入,因为scanln是以回车为标志结束
    
    n, err := fmt.Scanln(&name) //这样写就可以分两行输入name和age
    n, 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 main

import "fmt"

//假如我们要输入一个人的年龄和名字
func main()  {
    
    
	var name string
	var age int8
	fmt.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 main

import "fmt"

//假如我们要输入一个人的年龄和名字
func main()  {
    
    
	var name string
	var age int8
	fmt.Scan(&name)
	fmt.Scanln()
	fmt.Scanf("%d", &age)
	
}
}

}


```go
package main

import "fmt"

//假如我们要输入一个人的年龄和名字
func main()  {
	var name string
	var age int8
	fmt.Scan(&name)
	fmt.Scanln()
	fmt.Scanf("%d", &age)
	
}

猜你喜欢

转载自blog.csdn.net/qq_52698632/article/details/113747479