这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天。
这篇笔记旨在复习和分析go后端第一节课留下的三个作业:
1、修改猜谜游戏的代码,使用fmt.scnaf简化代码
2、修改在线词典的代码,增加另一种翻译引擎
3、在上一步的基础上,修改代码并行请求两个翻译引擎来提高响应速度
1、猜谜游戏
猜谜游戏中的输入流代码十分复杂,可以直接用fmt.scanf来简化。首先来看看scanf的定义。
func Scanf(format string, a ...interface{}) (n int, err error)
scanf函数根据format参数指定的格式将输入内容传值给参数a,返回值n是扫描成功的数量,error是遇到的错误。
对于猜谜游戏,我们要输入的是int型变量,因此猜谜游戏的输入代码如下:
var guess int
_, err := fmt.Scanf("%d\n", &guess)
if err != nil {
fmt.Println("An error occured while reading input. Please try again.Error is", err)
}
上述代码中,我不需要扫描成功的数量,因此用_代替变量。我要输入的是整数,因此scanf的format参数是%d。
运行结果如下:
2、在线词典——有道词典
我选择的第二个翻译引擎是有道词典。
打开有道词典www.youdao.com/ ,随便输入一个单词,按f12打开开发者模式,在network里找到一个jsonapi开头的请求。它的payload和preview如图所示:
可以看到有道词典的参数有q,le,t,client,sign和keyfrom,要处理我们发起的翻译请求,只需要q和le这两个参数即可。
这里要注意的是有道词典用的是Form Data的方式传参发起请求,参数格式是"q=dance&le=en",如果是彩云小译的Request Payload方式,参数格式是Json格式。
我们将请求的curl复制,进入Convert curl commands to Go (curlconverter.com)将curl转换为Go语言。之后,为了将有道词典的Json格式的Response转换为Go语言结构体,进入JSON转Golang Struct - 在线工具 - OKTools转换。
有道词典的Resonse非常长,全部复制下来绝大部分代码都没有用,因为我们只需要单词的音标和翻译即可,因此需要在Preview里找到关键信息,经过寻找,我们需要的关键信息均在ec中,如图所示:
经过上述准备,就可以用go制作基于有道词典的在线词典了。代码如下:
结构体
type DictResponse2 struct {
Ec struct {
WebTrans []string `json:"web_trans"`
Special []struct {
Nat string `json:"nat"`
Major string `json:"major"`
} `json:"special"`
ExamType []string `json:"exam_type"`
Source struct {
Name string `json:"name"`
URL string `json:"url"`
} `json:"source"`
Word struct {
Usphone string `json:"usphone"`
Ukphone string `json:"ukphone"`
Ukspeech string `json:"ukspeech"`
Trs []struct {
Pos string `json:"pos,omitempty"`
Tran string `json:"tran"`
} `json:"trs"`
Wfs []struct {
Wf struct {
Name string `json:"name"`
Value string `json:"value"`
} `json:"wf"`
} `json:"wfs"`
ReturnPhrase string `json:"return-phrase"`
Usspeech string `json:"usspeech"`
} `json:"word"`
} `json:"ec"`
}
请求函数
func query2(word string) {
client := &http.Client{}
var data = strings.NewReader("q=" + word + "&le=en&client=web") //Form Data格式的参形式,直接用strings.NewReader()创建Reader对象即可
//以下代码由curl转go复制而来
req, err := http.NewRequest("POST", "https://dict.youdao.com/jsonapi_s?doctype=json&jsonversion=4", data)
if err != nil {
log.Fatal(err)
}
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/x-www-form-urlencoded")
req.Header.Set("Cookie", "OUTFOX_SEARCH_USER_ID=1474111096@10.112.57.88; OUTFOX_SEARCH_USER_ID_NCOO=1361789659.5254738")
req.Header.Set("Origin", "https://www.youdao.com")
req.Header.Set("Referer", "https://www.youdao.com/")
req.Header.Set("Sec-Fetch-Dest", "empty")
req.Header.Set("Sec-Fetch-Mode", "cors")
req.Header.Set("Sec-Fetch-Site", "same-site")
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36")
req.Header.Set("sec-ch-ua", `"Not_A Brand";v="99", "Google Chrome";v="109", "Chromium";v="109"`)
req.Header.Set("sec-ch-ua-mobile", "?0")
req.Header.Set("sec-ch-ua-platform", `"Windows"`)
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)
}
//以上代码由curl转go复制而来
if resp.StatusCode != 200 {
log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
}
var dictResponse2 DictResponse2
err = json.Unmarshal(bodyText, &dictResponse2)
if err != nil {
log.Fatal(err)
}
//找到音标的位置是Ukphone和UsPhone,翻译结果在Trs中
fmt.Println(word, "UK:", dictResponse2.Ec.Word.Ukphone, "US:", dictResponse2.Ec.Word.Usphone)
for _, item := range dictResponse2.Ec.Word.Trs {
fmt.Println(item)
}
}
主函数
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, `usage: simpleDict WORDexample: simpleDict hello`)
os.Exit(1)
}
word := os.Args[1]
query2(word)
运行结果如下:
3、并行请求两个翻译引擎
Go语言中的并行可以用代码go来实现。go func就相当于创建了一个GoRoutine协程,协程是轻量级的线程,是在用户态中的,即使创建大量的协程也不会影响到内核态,可以通过这些协程进行并行操作,这也是Go语言的一大特点——并行。并行执行代码可以有效提升代码运行效率。并行请求两个翻译引擎的代码如下:
var wg sync.WaitGroup
wg.Add(1) //只需要执行一个即可
go func(word string) {
query2(word)
defer wg.Done() //执行完成,计数器 - 1
}(word)
go func(word string) {
query(word)
defer wg.Done() //执行完成,计数器 - 1
}(word)
wg.Wait() //阻塞直到计数器为0
运行结果只会出现一个翻译结果,在我的实验结果中只出现了彩云小译的翻译,推测是请求彩云小译的速度比有道词典更快,运行结果如下:
4、总结
以上就是我对这次作业的总结和分析。本人水平有限,可能有的地方写的不对,欢迎大家指正!