猜谜游戏与在线词典实战 | 青训营笔记

109 阅读8分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天

1.1.猜谜游戏

  • 需求:程序首先会生成一个介于 0 到 100 之间的随机整数,然后提示玩家进行猜测。玩家每次输入一个数字,程序会告诉玩家这个猜测的值是高于还是低于那个秘密的随机数,并且让玩家再次猜测。如果猜对了,就告诉玩家胜利并且退出程序。

1.1.1.rand的使用(一)

  • 使用rand,但是未指定随机数种子,此时调用 rand.Intn() 每次返回的数字都一样
package main
​
import (
    "fmt"
    "math/rand"
)
​
func main() {
    maxNum := 100
    secretNumber := rand.Intn(maxNum)   // 获取随机数,但是发现每次返回的数字都一样
    fmt.Println("The secret number is ", secretNumber)
}

1.1.2.rand的使用(二)

  • 使用 rand.Seed() 指定随机数种子,这里使用时间戳作为随机数种子,然后再使用 rand.Intn() 获取随机数,就没问题了
package main
​
import (
    "fmt"
    "math/rand"
    "time"
)
​
func main() {
    // 1.获取一个0-100的随机数
    maxNum := 100
    rand.Seed(time.Now().UnixNano())    // 指定随机数种子
    secretNumber := rand.Intn(maxNum)   // 获取随机数
    fmt.Println("The secret number is ", secretNumber)
}

1.1.3.读取用户输入,并转成int整数

  • 读取用户输入
    • 使用标准输入文件,转成reader变量方便操作
    • 返回的结果包含结尾的换行符,需要去掉
    • 如果发生错误,直接返回
  • 使用 strconv.Atoi() 将用户输入转成int类型整数
package main
​
import (
    "bufio"
    "fmt"
    "math/rand"
    "os"
    "strconv"
    "strings"
    "time"
)
​
func main() {
    // 1.获取一个0-100的随机数
    maxNum := 100
    rand.Seed(time.Now().UnixNano())
    secretNumber := rand.Intn(maxNum)
    fmt.Println("The secret number is ", secretNumber)
​
    // 2.获取用户输入,即用户猜测的数字,并进行初步的处理
    fmt.Println("Please input your guess")
    reader := bufio.NewReader(os.Stdin) // 使用标准输入文件,转成reader变量方便操作
    input, err := reader.ReadString('\n')   // 返回的结果包含结尾的换行符,需要去掉
    if err != nil {     // 发生错误,直接返回
        fmt.Println("An error occured while reading input. Please try again", err)
        return
    }
    input = strings.TrimSuffix(input, "\n") // 去除尾部的\n
​
    // 3.将用户输入转成int类型整数
    guess, err := strconv.Atoi(input)
    if err != nil {     //  发生错误,提示用户输入整数
        fmt.Println("Invalid input. Please enter an integer value")
        return
    }
    fmt.Println("You guess is", guess)
}

1.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)
    input, err := reader.ReadString('\n')
    if err != nil {
        fmt.Println("An error occured while reading input. Please try again", err)
        return
    }
    input = strings.TrimSuffix(input, "\n")
​
    guess, err := strconv.Atoi(input)
    if err != nil {
        fmt.Println("Invalid input. Please enter an integer value")
        return
    }
    fmt.Println("You guess is", guess)
    // 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!")
    }
}

1.1.5.增加循环判断逻辑

  • 增加循环判断逻辑
  • 如果用户猜错,则重新生成一个数字,重新获取用户输入,然后进行比较
  • 直到用户猜对为止
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)
    
    // 1.死循环,直到用户猜对停止
    for {
        input, err := reader.ReadString('\n')
        if err != nil {
            fmt.Println("An error occured while reading input. Please try again", err)
            continue
        }
        input = strings.TrimSuffix(input, "\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   // 2.用户猜对,才会退出
        }
    }
}

1.2.在线词典

  • 需求:实现一个输入英文,返回中文翻译的程序

1.2.1.获取彩云小译API

  • 使用浏览器打开网址:fanyi.caiyunapp.com/#/
  • 随便输入一个单词,自动翻译时会发生请求,抓包post请求api,包名称为dict

image.png

1.2.2.复制请求的cURL(bash)命令,使用工具生成GO代码

image.png

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
  • 打开网址:curlconverter.com/#go ,输入上述shell命令,选择转换成Go语言,即可自动生成代码

image.png

  • 生成代码如下,即为第一版代码。

    • 运行代码,可以打印出返回结果
package main
​
import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "strings"
)func main() {
    // 1.创建了一个 HTTP client,创建的时候也可以指定参数
    client := &http.Client{}    
    
    // 2.构造一个 HTTP 请求
    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)  // log.Fatal(),相当于先调用Print(),然后调用os.Exit(1)
    }
    
    // 设置请求头的一些信息
    req.Header.Set("Accept", "application/json, text/plain, */*")
    req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
    req.Header.Set("Connection", "keep-alive")
    req.Header.Set("Content-Type", "application/json;charset=UTF-8")
    req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
    req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
    req.Header.Set("Sec-Fetch-Dest", "empty")
    req.Header.Set("Sec-Fetch-Mode", "cors")
    req.Header.Set("Sec-Fetch-Site", "cross-site")
    req.Header.Set("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")
    req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
    req.Header.Set("app-name", "xy")
    req.Header.Set("os-type", "web")
    req.Header.Set("sec-ch-ua", `" Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"`)
    req.Header.Set("sec-ch-ua-mobile", "?0")
    req.Header.Set("sec-ch-ua-platform", `"Windows"`)
    
    // 3.client.Do() 发送请求,接收响应 response
    resp, err := client.Do(req)
    if err != nil {     // 如果出错,先打印错误信息,然后直接退出进程
        log.Fatal(err)
    }
    defer resp.Body.Close()     // responsebody 也是一个流,为了避免资源泄露,需要加一个 defer 来手动关闭这个流
    
    // 4.处理响应
    bodyText, err := ioutil.ReadAll(resp.Body)  // 读取body中的流数据,存入bodyText
    if err != nil {
        log.Fatal(err)      // 如果出错,先打印错误信息,然后直接退出进程
    }
    fmt.Printf("%s\n", bodyText)    // 打印响应体
}

1.2.3.生成请求消息体

  • 生成请求消息体

    • 前面的消息体,我们是写死的 {"trans_type":"en2zh","source":"good"} ,我们应该使用一个结构体保存数据
    • 创建一个 结构体 DictRequest,包含三部分数据:从什么语言转成什么语言、要翻译的数据、登录用户的ID
    • 然后把代码中,请求体先构建成一个DictRequest结构,然后再使用 json.Marshal() 转成json
package main
​
import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)type DictRequest struct {
    TransType string `json:"trans_type"`
    Source    string `json:"source"`
    UserID    string `json:"user_id"`
}
​
func main() {
    client := &http.Client{}
    
    // 请求体先构建成一个DictRequest结构,然后再使用 json.Marshal() 转成json
    request := DictRequest{TransType: "en2zh", Source: "good"}
    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)
    }
    fmt.Printf("%s\n", bodyText)
}

1.2.4.解析响应消息体

  • 发送请求后,得到的响应体,为了方便后续使用,我们需要创建一个结构体 DictResponse,封装响应体数据
  • 可 响应体内容太多了,手动创建结构体 麻烦费时还易出错,因此可以使用 工具 将响应体json转成Golang结构体
  • 打开网址:oktools.net/json2go,输入响应体数据,点击转换-嵌套,即可生成 结构体,改名为 DictResponse 即可
  • 然后在程序最后,将得到的响应体数据,使用 json.Unmarshal() 封装成 DictResponse 结构,方便使用

image.png

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

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

// 1、工具生成结构体,改名为 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"`
}

func main() {
	client := &http.Client{}
	request := DictRequest{TransType: "en2zh", Source: "good"}
	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)
	}
    
    // 2.将得到的响应体数据,封装成 DictResponse 结构
	var dictResponse DictResponse
	err = json.Unmarshal(bodyText, &dictResponse)	// 注意使用取地址符号,直接操作结构体
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%#v\n", dictResponse)
}

1.2.5.封装方法,封装可执行程序

  • 将上述代码封装成方法 query(word string),需要一个参数,就是待翻译的文本
  • 主方法,可以采用 命令行参数 传入参数,os.Args[0]表示当前执行的可执行文件,那么可以在os.Args[1]的位置传入参数。
  • 主方法获取os.Args[1],如果获取成功,则作为 query() 的参数,调用query()查询结果并打印
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"`
}

// 1.将上述代码封装成方法 `query(word string)`,需要一个参数,就是待翻译的文本
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)
	}
}

// 2.主方法获取os.Args[1],如果获取成功,则作为 query() 的参数,调用query()查询结果并打印
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)
}
  • 在终端运行命令

image.png