go语言实践
在学习go的基本语法后,就要开始实践了,这篇先记录在第二节课中讲到:猜数字
猜数字
首先先捋清楚这个代码的基本逻辑,很简单,就是用随机函数生成一个数字,然后通过无限循环的方式不断让用户输入一个数字,直到用户输入到正确的数字为止。
生成随机数
go语言中,通过
var res = rand.Intn(maxNum)
来生成一个随机数并且存放在变量res中
maxNum为生成随机数的上限
但是在c语言的学习中,我们知道代码中的随机函数都是伪随机的,本质上是通过一些数字生成随机数种子来进行随机数生成的,常用的随机种子就是当前的时间,同理这里我们也需要设置随机数种子,不然的话就会一直生成同一个数字,生成随机数种子的代码如下
rand.Seed(time.Now().UnixNano()) //用系统时间初始化随机数种子
用户输入
在聊go语言前,我们先回忆一下c++的输入方式,常见的有两种 一种是scanf输入,另一种是cin输入,后者是通过流来输入 在c++中,我们用scanf输入,经常会发现尾部的换行符没有读入,导致影响到了下一次读入。
在go语言中,也有scanf函数输入,也存在同样的问题
比如说如下代码:
for {
fmt.Println("please input your guess")
var num int
fmt.Scanf("%d", &num)
fmt.Println("your input is:", num)
if num == res {
fmt.Println("you are right")
break
} else if num > res {
fmt.Println("please guess a small number")
} else {
fmt.Println("please guess a big number")
}
}
输出的结果为:
please input your guess
12
your input is: 12
please guess a small number
please input your guess
your input is: 0
please guess a big number
please input your guess
可以发现,在我们输入一个12后,这个循环执行了两次,第一次正常读入12,第二次读入了0
其实第二次的0就是读入时的换行符(或者空格)导致的,当我们把scanf中修改为:
fmt.Scanf("%d%s", &num)
这时候的输出就是:
please input your guess
12
your input is: 12
please guess a big number
please input your guess
这里我们把后面的空格还有回车读入了,但是不存起来,就不会出现这样的问题。 这里我们看看scanf的原型函数:
其实scanf两个返回值n和err,n是按指定格式成功输入数据的个数,err是读取过程中遇到的错误,如果没有错误,err的值就为nil。
我们来尝试一下第一个代码到底发送了什么:
fmt.Scanf("%d%s", &num, &s) //需要两个%d后面那个读入回车符号,也可以改成%s
fmt.Println("your input is:", s)
输入12 输出为:your input is: 虽然看起来啥都没有,但是其实这里是有一个回车符号。
除了scanf,go中还有scan和scanln两个输入。
既然把scanf函数搞懂了,再来看看scan和scanln又是怎么回事 首先,它们的函数原型
跟scanf差不多,都是有两个返回值,一个是读取成功个数,另一个是错误值
但是如果像刚刚那样用会发生什么
for {
fmt.Println("please input your guess")
var num int
fmt.Scan(&num) //需要两个%d后面那个读入回车符号,也可以改成%s
fmt.Println("your input is:", num)
if num == res {
fmt.Println("you are right")
break
} else if num > res {
fmt.Println("please guess a small number")
} else {
fmt.Println("please guess a big number")
}
}
输出为:(12是输入)
please input your guess
12
your input is: 12
please guess a big number
please input your guess
用scanln的输出为:
please input your guess
12
your input is: 12
please guess a big number
please input your guess
其实scanln再换行的时候会把缓冲区的回车也收走,但是scan和scanf不会,所以就导致了scanf不能分多行输入数据。但是scan却可以,它虽然没有收走缓冲区的回车符,但是不会把回车符读进去,遇到回车它会继续读取下一个数据,而scanf会按照我们给的格式(如%d去读取数据),但是肯定读不进去的,所以就读取失败了
总结一下 scanf:按照给定的格式依次读取数据(包括非法数据),不能换行输入(如果要换行需要在前面加一个scanln吸收掉回车符,就像c语言中的getchar) scan:比scanf高级,依次读取数据,遇到回车会忽略,可以换行输入(如果要先用了scan输入,再用scanf输入的话,需要在中间加一个scanln) scanln:类似scan,但是遇到换行(回车)立马结束输入,如果要换行输入必须用多个scanln
除了用scanf输入,在c++中我们常用流来输入,就是用cin
在go语言中也有同样的操作,每个程序执行的时候都会打开几个文件,stdin stdout stderr等,stdin 文件(也就是输入流)可以用 os.Stdin 来得到。
但是,直接操作这个文件很不方便,我们会用 bufio.NewReader 把一个文件转换成一个 reader 变量。 reader 会有很多用来操作流的操作,我们可以用它的 ReadString 方法来读取一行。 如果读取失败,则打印错误并退出。注意,ReadString 返回的结果包含结尾的换行符,我们应当先把它去掉,再转换成数字。如果转换失败,则同样打印错误,退出。
代码如下:
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("An error occured while reading input. Please try again", err)
return
}
input = strings.Trim(input, "\r\n") //去掉换行符 CR-LF
num, err := strconv.Atoi(input) //尝试将输入转换为数字
if err != nil {
fmt.Println("Invalid input. Please enter an integer value")
return
}
这样我们就可以完成输入,当然这里需要增加头文件bufio
完整的猜数字代码如下:(这里是用Scanln的)用reader,ReadString的只需要修改一下即可
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
maxNum := 105
rand.Seed(time.Now().UnixNano()) //用系统时间初始化随机数种子
var res = rand.Intn(maxNum)
fmt.Println(res)
for {
fmt.Println("please input your guess")
var num int
fmt.Scanln(&num) //需要两个%d后面那个读入回车符号,也可以改成%s
fmt.Println("your input is:", num)
if num == res {
fmt.Println("you are right")
break
} else if num > res {
fmt.Println("please guess a small number")
} else {
fmt.Println("please guess a big number")
}
}
}