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

59 阅读7分钟

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

项目实战

1.猜谜游戏

1.1 生成随机数

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

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

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    maxNum := 100
    secretNumber := rand.Intn(maxNum)
    fmt.Println("The secret number is ", secretNumber)
}

1.2 随机种子

采用上述代码生成数字,我们会发现每次产生的随机数都一样。这是因为程序内部存在一个随机种子,以此作为初始条件,然后用一定的算法不停迭代产生随机数,随机种子不变,则产生的随机数也是固定的。

因此我们需要调用rand.Seed(xxxx)函数,并以时间戳time.now.unix作为随机种子。

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    maxNum := 100
    rand.Seed(time.Now().UnixNano())
    secretNumber := rand.Intn(maxNum)
    fmt.Println("The secret number is ", secretNumber)
}

现在可以正常输出0-100之间的随机数了。

1.3 读取用户输入

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

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

1.4 判断逻辑

将用户所猜的数字与被猜数进行比较判断,并且返回比较结果。

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

1.5 实现循环

最后我们希望我们这个程序在用户猜到数字之后再退出,于是我们在我们输入的位置加上一个无限循环的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.在线词典

2.1 抓包

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

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

检查F12.png

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

dict.png

由于我们需要发送这个HTTP请求,但是用代码构建又会很麻烦,因此我们只需要右键选择dict,点击Copy,点击Copy as cURL(bash)

curl "https://api.interpreter.caiyunai.com/v1/dict" ^
  -H "Accept: application/json, text/plain, */*" ^
  -H "Accept-Language: zh-CN,zh;q=0.9" ^
  -H "Connection: keep-alive" ^
  -H "Content-Type: application/json;charset=UTF-8" ^
  -H "Origin: https://fanyi.caiyunapp.com" ^
  -H "Referer: https://fanyi.caiyunapp.com/" ^
  -H "Sec-Fetch-Dest: empty" ^
  -H "Sec-Fetch-Mode: cors" ^
  -H "Sec-Fetch-Site: cross-site" ^
  -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36" ^
  -H "X-Authorization: token:qgemv4jr1y38jyq6vhvi" ^
  -H "app-name: xy" ^
  -H "device-id: " ^
  -H "os-type: web" ^
  -H "os-version: " ^
  -H "sec-ch-ua: ^^" Not A;Brand^^";v=^^"99^^", ^^"Chromium^^";v=^^"101^^", ^^"Google Chrome^^";v=^^"101^^"" ^
  -H "sec-ch-ua-mobile: ?0" ^
  -H "sec-ch-ua-platform: ^^"Windows^^"" ^
  --data-raw "^{^^"trans_type^^":^^"en2zh^^",^^"source^^":^^"good^^"^}" ^
  --compressed

2.2 生成代码

通过Convert curl commands to code (curlconverter.com)这个网页,将代码粘贴到curl command,就可以获得以下代码(已加入解释注释)。有几个header比较复杂,生成代码有转义导致的编译错误,删掉这几行即可。

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)
    }
    // 关闭Body这个流,避免资源泄露
    defer resp.Body.Close()
    // 读取响应
    bodyText, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s\n", bodyText)
}

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

2.3 request body

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

为了使让我们的输入不再固定,因此我们需要创建一个结构体来代替我们的输入,对比请求代码,我们需要两个数据,一个是翻译类型,一个是被翻译对象。因此我们创建以下结构体,并使用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)

2.4 完整代码

我们将其包装成一个函数,并且需要传入一个 string 类型的值,作为被翻译的字符串,最后通过main主函数来进行测试其功能,完整代码如下:

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)
    }
    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)
    }
    // 关闭Body这个流,避免资源泄露
    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)
}