Go入门--猜数字游戏和词典在线翻译 | 青训营笔记

183 阅读4分钟

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

一、初识Go文件结构---输出Hello World!

Go语言开发环境配置参考:Go 语言原理与实践学习资料

1.1 创建xx.go文件

  1. 所有的 .go 文件,除了空行和注释,都应该在第一行声明自己所属的包
  2. 源文件头部以package xxx声明包名称,即一个目录下的同级源文件属于同一个包
  3. 包所在的目录名最好不用 main 、 all 、 std 这三个保留名称!!!!
  4. 可执行文件必须包含 package main 和入口函数 main ,  main 包是 Go 语言程序的入口包,一个 Go 语言程序必须有且仅有一个 main 包,并且,一个 main 包中也必须有且仅有一个 main 函数。 Plus: 权限问题 包中成员以名称首字母大小写决定访问权限
  5. Public : 首字母大写,可被包外访问
  6. internal : 首字母小写,仅包内成员可以访问

1.2 敲代码Demo---Hello World!

package main
import (
	"fmt"
)
func main(){
    fmt.Println("hello world!")
}

运行方式:go run xxx.go

二、简易的猜数字游戏

因为每次需要生成一个随机数,所以需要导入包math/rand

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

main函数中加入下述代码,注意生成随机数需要设置随机数种子,这里采用了时间戳

func main() {
	maxNum := 100  //随机数的范围
	rand.Seed(time.Now().UnixNano())//时间戳作为随机数种子
	secretNumber := rand.Intn(maxNum)
        ...//开始读取用户键盘输入
}

接下来,就是利用for循环来判断用户猜测的数字是否正确,返回对应的猜测结果,考察了if分支和for循环,以及程序数据的读入,输出格式等

func main() {
        ...//生成猜测数
        var guess int
	_, err := fmt.Scanf("%d\r\n", &guess)//在windows系统中这里是\r\n
	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//猜对了则退出for循环
	}
}

三、简易版的在线词典翻译

因为老师视频里面采用的是彩云翻译:fanyi.caiyunapp.com/#/ 这里我调用的是火山翻译:translate.volcengine.com/translate

1. 抓包分析请求API

首先,我们需要找到那个调用翻译API的请求,这边我是通过点击图片左边捕捉的一些请求,挨个查看它们各自对应的响应,看返回的数据是否是预期的 image.png

2. 自动生成请求,响应结构体

在找到的那个请求上右键->复制->以cURL(bash)格式复制,这部分内容通过一个网址解析,可以为我们自动生成对应的Go代码

通过视频讲解,明显需要两个结构体:一个请求结构体,一个响应结构体 请求结构体由红框处构造 image.png 请求结构体定义如下:

type HuoShanRequest struct {
	Text     string `json:"text"`
	Language string `json:"language"`
}

而响应结构体,我们则需要复制预览里面的,通过另外一个网址解析json数据

type HuoShanReponse struct {
	Words []struct {
		Source  int    `json:"source"`
		Text    string `json:"text"`
		PosList []struct {
			Type      int `json:"type"`
			Phonetics []struct {
				Type int    `json:"type"`
				Text string `json:"text"`
			} `json:"phonetics"`
			Explanations []struct {
				Text     string `json:"text"`
				Examples []struct {
					Type      int `json:"type"`
					Sentences []struct {
						Text      string `json:"text"`
						TransText string `json:"trans_text"`
					} `json:"sentences"`
				} `json:"examples"`
				Synonyms []interface{} `json:"synonyms"`
			} `json:"explanations"`
			Relevancys []interface{} `json:"relevancys"`
		} `json:"pos_list"`
	} `json:"words"`
	Phrases  []interface{} `json:"phrases"`
	BaseResp struct {
		StatusCode    int    `json:"status_code"`
		StatusMessage string `json:"status_message"`
	} `json:"base_resp"`
}

实现的调用火山翻译API的接口函数

func queryByHuoShan(word string) {
        //初始化数据请求结构体
	data := HuoShanRequest{
		// fill struct
		Text:     word,
		Language: "en",
	}
	payloadBytes, err := json.Marshal(data)//序列化
	if err != nil {
		// handle err
		log.Fatal(err)
	}
	body := bytes.NewReader(payloadBytes)
	req, err := http.NewRequest("POST", "https://translate.volcengine.com/web/dict/match/v1/", body)
	if err != nil {
		// handle err
		log.Fatal(err)
	}
	req.Header.Set("Authority", "translate.volcengine.com")
	req.Header.Set("Accept", "application/json, text/plain, */*")
	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Referer", "https://translate.volcengine.com")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		// handle err
		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 hsp HuoShanReponse
	err = json.Unmarshal(bodyText, &hsp)//将响应的字节流数据反序列化成响应结构体
	if err != nil {
		log.Fatal(err)
	}
        //核心修改代码
        //这里需要注意的是结构体的嵌套关系,从而确定翻译和发音的正确位置
	fmt.Println(word, "UK: ", hsp.Words[0].PosList[0].Phonetics[0].Text, "US: ", hsp.Words[0].PosList[0].Phonetics[1].Text)
	for _, item := range hsp.Words[0].PosList[0].Explanations {
		fmt.Printf("%s\n", item.Text)
	}
}

3. 修改代码实现并行请求两个翻译引擎来提高响应速度

常见的实现基于 waitGroup errGroup chan 参考链接:深入浅出 WaitGroup 和 Errgroup

  • 我的waitGroup版本
func main() {
	if len(os.Args) != 2 {
		fmt.Fprintf(os.Stderr, `usage: simpleDict WORD
example: simpleDict hello
		`)
		os.Exit(1)
	}
	word := os.Args[1]
	var wg sync.WaitGroup
	wg.Add(2)//添加2个子线程
	go func ()  {
		defer wg.Done()//通知主线程结束
		fmt.Println("-----彩云翻译-----")
		query(word)
	}()
	go func ()  {
		defer wg.Done()//通知主线程结束
		fmt.Println("-----火山翻译-----")
                queryByHuoShan(word)
	}()
	wg.Wait()
	fmt.Println("Finish.")
	// fmt.Println("-----彩云翻译-----")
	// query(word)
	// fmt.Println("-----火山翻译-----")
	// queryByHuoShan(word)
}