这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天
3.1 猜谜游戏
在这个游戏里面,程序首先会生成一个介于1到100之间的随机整数,然后提示玩家进行猜测。玩家每次输入一个数字,程序会告诉玩家这个猜测的值是高于还是低于那个秘密的随机数,并且让玩家再次猜测。如果猜对了,就告诉玩家胜利并且退出程序。
3.1.1 生成随机数
math/rand是 Go 语言中专门用来做与随机数相关操作的包,主要有生成随机数、随机种子等方法
rand.Intn(n)生成一个值在 [0, n) 区间的随机整数rand.Seed(seed)以设置随机种子,seed 为int64类型
time.Now().UnixNano()是获取当前时间的时间戳(以纳秒为单位)
package main
import (
"fmt"
"math/rand"
)
func main() {
// 规定随机数的最大区间范围
maxNum := 100
// 随机生成[0, maxNum)区间中的一个整数
secretNumber := rand.Intn(maxNum)
// 打印该随机数字
fmt.Println("The secret number is ", secretNumber)
// 但是如果你多执行几次,会发现一直都是81这个数字,why?因为我们没有设置随机数种子
// 使用math/rand之前需要设置随机数种子,否则的话每一次都会生成相同的随机数序列。
// 一般惯例用法是在程序启动的时候,用启动的时间戳来初始化随机数种子。
}
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
// 规定随机数的最大区间范围
maxNum := 100
// 设置随机数种子,将种子设置为当前时间戳
rand.Seed(time.Now().UnixNano())
// 随机生成[0, maxNum)区间中的一个整数
secretNumber := rand.Intn(maxNum)
// 打印该随机数字
fmt.Println("The secret number is ", secretNumber)
// 多执行几次,就不再会是同一个数字了
}
3.1.2 读取用户输入
读取用户键盘输入
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
"time"
)
func main() {
// ------系统生成随机数部分-----
maxNum := 100
rand.Seed(time.Now().UnixNano())
secretNumber := rand.Intn(maxNum)
fmt.Println("The secret number is ", secretNumber)
// ------读取用户输入部分--------
// 提示用户输入猜测数字
fmt.Println("Please input your guess")
reader := bufio.NewReader(os.Stdin)
// 进行错误处理
input, err := reader.ReadString('\n')
// 如果err不为空,那么就说明执行过程中返回了错误信息,那就直接打印出来
if err != nil {
fmt.Println("An error occured while reading input. Please try again", err)
return
}
// 去掉换行符,将字符串首尾的 \r 和 \n 去掉
input = strings.Trim(input, "\r\n")
// 将input转化为数字
guess, err := strconv.Atoi(input)
// 进行错误处理
if err != nil {
// 打印错误信息
fmt.Println("Invalid input. Please enter an integer value")
return
}
// 输出用户输入数字
fmt.Println("You guess is", guess)
}
3.1.3 实现判断逻辑
就多加了一个if大小判断
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
"time"
)
func main() {
// 这里都是3.1.2中的代码
// 下面是实现判断逻辑的代码
// 将用户猜测数字与系统生成的随机数进行对比
if guess > secretNumber {
// 如果用户猜测较大,提示用户随机数应该较小一些
fmt.Println("Your guess is bigger than the secret number. Please try again")
} else if guess < secretNumber {
// 如果用户猜测较小,提示用户随机数应该较大一些
fmt.Println("Your guess is smaller than the secret number. Please try again")
} else {
// 最后剩下的情况就只有两者相等,这时直接输出结果即可
fmt.Println("Correct, you Legend!")
}
}
3.1.4 实现游戏循环
实现猜测的一直连续,直到猜中为止
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
"time"
)
func main() {
//-------系统生成随机数--------
maxNum := 100
rand.Seed(time.Now().UnixNano())
secretNumber := rand.Intn(maxNum)
// fmt.Println("The secret number is ", secretNumber)
// ------读取用户输入部分-------
fmt.Println("Please input your guess")
reader := bufio.NewReader(os.Stdin)
// 这里for不加任何东西就相当于while循环,且是while(true)死循环
// 将之前所说的所有操作整合到下述for循环中
for {
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("An error occured while reading input. Please try again", err)
// 将break换成了continue,因为考虑到break会终止for循环的,只需要跳过本次循环就行了
continue
}
input = strings.Trim(input, "\r\n")
guess, err := strconv.Atoi(input)
if err != nil {
fmt.Println("Invalid input. Please enter an integer value")
continue
}
fmt.Println("You guess is", guess)
if guess > secretNumber {
fmt.Println("Your guess is bigger than the secret number. Please try again")
} else if guess < secretNumber {
fmt.Println("Your guess is smaller than the secret number. Please try again")
} else {
fmt.Println("Correct, you Legend!")
break
}
}
}
3.2 在线词典
用户在命令行里面查询一个单词,通过调用第三方的 API 查询到单词的翻译并打印出来,在这个项目中我们将学会如何使用 Go 语言来发送 HTTP 请求、解析 JSON,还会学习如何使用代码生成来提高开发效率。
3.2.1 抓包
以彩云科技提供的在线翻译为例,直接右键检查打开浏览器的开发者工具(这个应该都懂吧)。
这是 HTTP 的一个 post 请求,API 的返回结果里会有 dictionary 和 wiki 两个字段,我们想要的数据就在 dictionary.explanations 字段里,其他字典还包括该单词的其他信息,例如音标。。。
右键 dict,以 cURL 格式进行复制,在命令行粘贴出来,应该可以看到一大串的 json
curl 'https://api.interpreter.caiyunai.com/v1/dict' \ -H 'authority: api.interpreter.caiyunai.com' \ -H 'sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="99", "Google Chrome";v="99"' \ -H 'os-version: ' \ -H 'sec-ch-ua-mobile: ?0' \ -H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36' \ -H 'app-name: xy' \ -H 'content-type: application/json;charset=UTF-8' \ -H 'accept: application/json, text/plain, */*' \ -H 'device-id: ' \ -H 'os-type: web' \ -H 'x-authorization: token:qgemv4jr1y38jyq6vhvi' \ -H 'sec-ch-ua-platform: "Windows"' \ -H 'origin: https://fanyi.caiyunapp.com' \ -H 'sec-fetch-site: cross-site' \ -H 'sec-fetch-mode: cors' \ -H 'sec-fetch-dest: empty' \ -H 'referer: https://fanyi.caiyunapp.com/' \ -H 'accept-language: zh-CN,zh;q=0.9,zh-TW;q=0.8' \ --data-raw '{"trans_type":"en2zh","source":"good"}' \ --compressed
3.2.2 代码生成
这里要用到一个在线代码自动生成的网站curlconverter.com/go/,添加 curl 命令,自动生成相应命令操作的 go 代码,很大地提高了开发效率。
// 将上面的curl代码通过代码生成网站转化为的go代码
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
)
func main() {
// 创建HTTP client,可以指定很多参数,例如请求超时是否使用cookie。。。
client := &http.Client{}
// 构造HTTP请求的请求体,自定义相关参数
// trans_type: 翻译方式,en2zh:英语翻译为中文
// source: 想要查询的单词
var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)
// 构造一个HTTP请求,并且的post请求,https://api.interpreter.caiyunai.com/v1/dict为发送请求的url
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
// 错误处理,请求无异常err为nil,如果中途出现问题,err为返回错误信息
if err != nil {
// 直接返回错误信息并结束程序
log.Fatal(err)
}
// 设置请求体中的一大堆参数
req.Header.Set("authority", "api.interpreter.caiyunai.com")
req.Header.Set("sec-ch-ua", `" Not A;Brand";v="99", "Chromium";v="99", "Google Chrome";v="99"`)
req.Header.Set("sec-ch-ua-mobile", "?0")
req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36")
req.Header.Set("app-name", "xy")
req.Header.Set("content-type", "application/json;charset=UTF-8")
req.Header.Set("accept", "application/json, text/plain, */*")
req.Header.Set("os-type", "web")
req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
req.Header.Set("sec-ch-ua-platform", `"Windows"`)
req.Header.Set("origin", "https://fanyi.caiyunapp.com")
req.Header.Set("sec-fetch-site", "cross-site")
req.Header.Set("sec-fetch-mode", "cors")
req.Header.Set("sec-fetch-dest", "empty")
req.Header.Set("referer", "https://fanyi.caiyunapp.com/")
req.Header.Set("accept-language", "zh-CN,zh;q=0.9,zh-TW;q=0.8")
// 发送请求
resp, err := client.Do(req)
// 错误处理,判断请求是否发送成功并且服务器响应
if err != nil {
// 如果出现问题,打印错误信息并结束程序
log.Fatal(err)
}
// 关闭client,释放资源
defer resp.Body.Close()
// 读取服务器的响应结果
bodyText, err := ioutil.ReadAll(resp.Body)
// 错误处理
if err != nil {
log.Fatal(err)
}
// 打印出服务器的响应结果,也就是输入单词的中文翻译
fmt.Printf("%s\n", bodyText)
}
// 格式化后的bodyText
{
"rc": 0,
"wiki": {
"known_in_laguages": 63,
"description": {
"source": "tangible and intangible thing, except labor tied services, that satisfies human wants and provides utility",
"target": null
},
"id": "Q28877",
"item": {
"source": "good",
"target": "\u5546\u54c1"
},
"image_url": "http://www.caiyunapp.com/imgs/link_default_img.png",
"is_subject": "true",
"sitelink": "https://www.caiyunapp.com/read_mode/?id=6354777915466339550246c5"
},
"dictionary": {
"prons": {
"en-us": "[g\u028ad]",
"en": "[gud]"
},
"explanations": ["a.\u597d\u7684;\u5584\u826f\u7684;\u5feb\u4e50\u7684;\u771f\u6b63\u7684;\u5bbd\u5927\u7684;\u6709\u76ca\u7684;\u8001\u7ec3\u7684;\u5e78\u798f\u7684;\u5fe0\u5b9e\u7684;\u4f18\u79c0\u7684;\u5b8c\u6574\u7684;\u5f7b\u5e95\u7684;\u4e30\u5bcc\u7684", "n.\u5229\u76ca;\u597d\u5904;\u5584\u826f;\u597d\u4eba", "ad.=well"],
"synonym": ["excellent", "fine", "nice", "splendid", "proper"],
"antonym": ["bad", "wrong", "evil", "harmful", "poor"],
"wqx_example": [
["to the good", "\u6709\u5229,\u6709\u597d\u5904"],
["good, bad and indifferent", "\u597d\u7684,\u574f\u7684\u548c\u4e00\u822c\u7684"],
["good innings", "\u957f\u5bff"],
["good and ...", "\u5f88,\u9887;\u5b8c\u5168,\u5f7b\u5e95"],
["do somebody's heart good", "\u5bf9\u67d0\u4eba\u7684\u5fc3\u810f\u6709\u76ca,\u4f7f\u67d0\u4eba\u611f\u5230\u6109\u5feb"],
["do somebody good", "\u5bf9\u67d0\u4eba\u6709\u76ca"],
["be good for", "\u5bf9\u2026\u6709\u6548,\u9002\u5408,\u80dc\u4efb"],
["be good at", "\u5728\u2026\u65b9\u9762(\u5b66\u5f97,\u505a\u5f97)\u597d;\u5584\u4e8e"],
["as good as one's word", "\u4fe1\u5b88\u8bfa\u8a00,\u503c\u5f97\u4fe1\u8d56"],
["as good as", "\u5b9e\u9645\u4e0a,\u51e0\u4e4e\u7b49\u4e8e"],
["all well and good", "\u4e5f\u597d,\u8fd8\u597d,\u5f88\u4e0d\u9519"],
["a good", "\u76f8\u5f53,\u8db3\u8db3"],
["He is good at figures . ", "\u4ed6\u5584\u4e8e\u8ba1\u7b97\u3002"]
],
"entry": "good",
"type": "word",
"related": [],
"source": "wenquxing"
}
}
3.2.3 生成 request body
我们需要生成一段 JSON,常用的方式是我们先构造出来一个结构体,这个结构体和我们需要生成的JSON 的结构是——对应的
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)
// 创建构造体,类型后的`json:name`是JSON序列化后的名称
type DictRequest struct {
TransType string `json:"trans_type"`
Source string `json:"source"`
UserID string `json:"user_id"`
}
func main() {
client := &http.Client{}
// 初始化请求结构体
request := DictRequest{TransType: "en2zh", Source: "good"}
// 使用json.Marshal得到结构体序列化后的字符串
buf, err := json.Marshal(request)
if err != nil {
log.Fatal(err)
}
var data = bytes.NewReader(buf)
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
if err != nil {
log.Fatal(err)
}
// 后面代码和上面的设置请求体参数及以后代码一样
}
3.2.4 解析 response body
在线工具—OKTools => 点击
JSON 转 Go Struct,然后点击转化-嵌套
// 将之前的bodyText转化后的结构体
type AutoGenerated struct {
Rc int `json:"rc"`
Wiki struct {
KnownInLaguages int `json:"known_in_laguages"`
Description struct {
Source string `json:"source"`
Target interface{} `json:"target"`
} `json:"description"`
ID string `json:"id"`
Item struct {
Source string `json:"source"`
Target string `json:"target"`
} `json:"item"`
ImageURL string `json:"image_url"`
IsSubject string `json:"is_subject"`
Sitelink string `json:"sitelink"`
} `json:"wiki"`
Dictionary struct {
Prons struct {
EnUs string `json:"en-us"`
En string `json:"en"`
} `json:"prons"`
Explanations []string `json:"explanations"`
Synonym []string `json:"synonym"`
Antonym []string `json:"antonym"`
WqxExample [][]string `json:"wqx_example"`
Entry string `json:"entry"`
Type string `json:"type"`
Related []interface{} `json:"related"`
Source string `json:"source"`
} `json:"dictionary"`
}
将自动生成的构造体替换掉之前我们假定的构造体,然后添加以下语句
// 定义结构体变量 var dictResponse DictRequest // 将得到的字符串反序列化为结构体 err = json.Unmarshal(bodyText, &dictResponse) if err != nil { log.Fatal(err) } // 更加详细的打印 fmt.Printf("%#v\n", dictResponse)
3.2.5 打印结果
// 最终的完整代码
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
// 包含用户查询单词的请求结构体
type DictRequest struct {
TransType string `json:"trans_type"`
Source string `json:"source"`
UserID string `json:"user_id"`
}
// 服务器返回的响应结构体
type DictResponse struct {
Rc int `json:"rc"`
Wiki struct {
KnownInLaguages int `json:"known_in_laguages"`
Description struct {
Source string `json:"source"`
Target interface{} `json:"target"`
} `json:"description"`
ID string `json:"id"`
Item struct {
Source string `json:"source"`
Target string `json:"target"`
} `json:"item"`
ImageURL string `json:"image_url"`
IsSubject string `json:"is_subject"`
Sitelink string `json:"sitelink"`
} `json:"wiki"`
Dictionary struct {
Prons struct {
EnUs string `json:"en-us"`
En string `json:"en"`
} `json:"prons"`
Explanations []string `json:"explanations"`
Synonym []string `json:"synonym"`
Antonym []string `json:"antonym"`
WqxExample [][]string `json:"wqx_example"`
Entry string `json:"entry"`
Type string `json:"type"`
Related []interface{} `json:"related"`
Source string `json:"source"`
} `json:"dictionary"`
}
// 提取出核心代码,整合为一个函数
func query(word string) {
client := &http.Client{}
request := DictRequest{TransType: "en2zh", Source: word}
buf, err := json.Marshal(request)
if err != nil {
log.Fatal(err)
}
// 这里返回的是byte数组
var data = bytes.NewReader(buf)
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Connection", "keep-alive")
req.Header.Set("DNT", "1")
req.Header.Set("os-version", "")
req.Header.Set("sec-ch-ua-mobile", "?0")
req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36")
req.Header.Set("app-name", "xy")
req.Header.Set("Content-Type", "application/json;charset=UTF-8")
req.Header.Set("Accept", "application/json, text/plain, */*")
req.Header.Set("device-id", "")
req.Header.Set("os-type", "web")
req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
req.Header.Set("Sec-Fetch-Site", "cross-site")
req.Header.Set("Sec-Fetch-Mode", "cors")
req.Header.Set("Sec-Fetch-Dest", "empty")
req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
req.Header.Set("Cookie", "_ym_uid=16456948721020430059; _ym_d=1645694872")
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
bodyText, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
// 判断接口的状态码是否为200
if resp.StatusCode != 200 {
log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
}
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
log.Fatal(err)
}
// 打印需求的部分
fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
// 循环遍历打印
for _, item := range dictResponse.Dictionary.Explanations {
fmt.Println(item)
}
}
func main() {
// 判断命令和参数的个数
if len(os.Args) != 2 {
// 不是2个,直接提示错误
fmt.Fprintf(os.Stderr, `usage: simpleDict WORD
example: simpleDict hello
`)
// 并结束程序
os.Exit(1)
}
// 否则,开始查询单词意思
word := os.Args[1]
query(word)
}
预期结果
未完待续。。。