Go 语言上手 - 基础语言(2)| 青训营笔记

169 阅读6分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记

三. 基础项目实战

1. 猜数游戏

通过简单的Go语言的基础学习,我们这次来实现一个猜数游戏

猜数游戏的本质就是,通过程序内部随机出一个数字,我们通过输入所猜的数字,判断并返回目标数与所猜数是否相等,如果相等则返回猜中信息,并退出程序,如果不相等则返回目标数与所猜数的大小关系。

首先我们为了实现随机出0~99之间的随机数,我们需要引入"math/rand"包,然后利用rand.Int()函数进行获取一个随机数,代码如下:

import (
    "fmt"
    "math/rand"
)
func main() {
    maxNum := 100
    secretNumber := rand.Intn(maxNum)
    fmt.Println("当前随机数为: ", secretNumber)
}

然而,我们会发现每次随机出来数都一样,这是因为每次没有调用rand.Seed(xxxx), 导致随机种子都是 1 ,所以如果想要每次随机值不一样,我们就需要用时间戳作为随机种子
因此形成以下代码:

import (
    "fmt"
    "math/rand"
    "time"
)
func main() {
    maxNum := 100
    rand.Seed(time.Now().UnixNano())                    //通过时间戳作为随机种子
    secretNumber := rand.Intn(maxNum)
    fmt.Println("当前随机数为: ", secretNumber)
}

接下来我们将实现如何读取用户输入的数据,并解析成数字,我们可以添加以下代码就能获取我们输入的数据,并判断与返回是否为数字类型

var guess int
_, err := fmt.Scanf("%d", &guess)                        //获取输入并判断是否为int类型
if err != nil {
    fmt.Println("不是数字类型,请输入数字")               //如果不是int类型则输出错误
    return
}

接下来我们需要将我们所猜的数字与被猜数进行比较判断,并且返回比较结果

if guess > secretNumber {
    fmt.Println("你猜的数字大于被猜数,请再输入一次")
} else if guess < secretNumber {
    fmt.Println("你猜的数字小于被猜数,请再输入一次")
} else {
    fmt.Println("恭喜你,猜中了这个数字")
}

最后我们希望我们这个程序在我们猜到数字之后再退出,于是我们在我们输入的位置加上一个无限循环的for语句,最后实现整个猜数字流程的代码就实现了

package main
import (
    "fmt"
    "math/rand"
    "time"
)
func main() {
    maxNum := 100
    rand.Seed(time.Now().UnixNano())
    secretNumber := rand.Intn(maxNum)
    fmt.Println("请输入你要猜的数字")
    for {
        var guess int	
        _, err := fmt.Scanf("%d", &guess)
        if err != nil {
            fmt.Println("不是数字类型,请输入数字") 
            continue
        }
        fmt.Println("You guess is", guess)
        if guess > secretNumber {
            fmt.Println("你猜的数字大于被猜数,请再输入一次")
        } else if guess < secretNumber {
            fmt.Println("你猜的数字小于被猜数,请再输入一次")
        } else {
            fmt.Println("恭喜你,猜中了这个数字")
            break                                          //当猜中数字后跳出死循环
        }
    }
}

2. 在线词典

熟练的掌握了 Go语言的各种包的使用,这次我们来使用第三方API来进行实现在线单词词典翻译功能,在这个项目当中,我们将利用 Go语言发送 HTTP 请求、解析 json 数据过来,并且学习如何使用代码生成来提高开发效率。

为了获取我们所需要的 API,我们需要通过抓包来获取
这次项目我们通过以彩云翻译为例,先打开彩云翻译的网页,然后右键网页选择检查或者按下 F12 来打开开发者工具:

image.png

然后我们找到网络一栏并且选择它

image.png

然后我们通过执行对 good单词进行翻译,仔细查看网络,找到一个dict的文件,点击预览就会发现 good单词通过API返回的 json数据

image.png

由于我们需要发送这个HTTP请求,但是用代码构建又会很麻烦因此我们只需要将dict右键找到复制(Copy),再选择复制为cURL(Copy as cURL),再通过Convert curl commands to code (curlconverter.com)这个网页,将代码粘贴到curl command,就可以获得以下代码(已加入解释注释)

package main
import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "strings"
)
func main() {
    client := &http.Client{}
    var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)
    //创建请求
    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)
    }
    fmt.Printf("%s\n", bodyText)
}

我们将这一串代码copy到我们的编译器当中即可,然后我们运行这段代码,就会清楚的发现我们是能够发送请求,并且返回了一大串JSON数据

image.png

但是我们需要制作一个结构体来承载这个JSON数据的格式,因此我们找到dict文件对其右键,选择复制,复制其响应,然后通过网页JSON转Golang Struct - 在线工具 - OKTools选择嵌套类型并改名为DictResponse,就可以得到我们想要的结构体作为响应结构体。

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"`
}

接下来,我们为了使让我们的输入不再固定,因此我们需要创建一个结构体来代替我们的输入,对比请求代码,我们需要两个数据,一个是翻译类型,一个是被翻译对象。因此我们创建以下结构体,并使用json tag对其进行JSON处理:

type DictRequest struct {
    TransType string `json:"trans_type"`
    Source    string `json:"source"`
}

然后我们通过初始化DictRequest结构体来发送请求数据,最后使用json.Unmarshal进行反序列化对返回的数据储存到DictResponse结构体中,我们就实现了如何发送要翻译的数据,并返回其翻译结果。如果想要获取返回数据的特定输出,我们可以通过打印其结构体的某一部分即可,例如我们想要获取我们翻译过后单词的英式读法,和美式读法,我们可以通过以下代码打印出来即可

fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)

最后我们将其包装成一个函数,并且需要传入一个 string 类型的值,作为被翻译的字符串.最后通过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)
	}
	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)
	}
	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 {
		fmt.Fprintf(os.Stderr, `usage: simpleDict WORD
example: simpleDict hello
		`)
		os.Exit(1)
	}
	word := os.Args[1]
	query(word)
}