Go语言基础上手(含作业详细解答)| 青训营笔记

1,435 阅读5分钟

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

课前导学链接

【Go 语言原理与实践学习资料】第三届字节跳动青训营-后端专场 - 掘金

本节课内容为Go语言基础和三个小项目实战,课程代码在以下网址中

github.com/wangkechun/…

课程PPT链接

Go语言基础上手

Go语言基础

第一部分的内容基本可以在以下链接中找到,建议基础薄弱的同学阅读以下链接。

Go by Example 中文版

此外贴出一个我非常推荐的一个Go语言教程,Go语言之旅

Go 语言之旅

小项目实战

项目一:猜谜游戏

第一个小项目猜数字游戏,这个项目比较简单,主要是联系一些基础语法使用,使用相应的包建议浏览官方文档,此项目用到了math/rand包。

项目二:简单词典

第二个小项目简单词典,利用彩云小译的查词API来查询英语单词。

首先展示了如何利用浏览器开发人员工具获取HTTP请求的详细信息(网络部分找到dict请求),复制dict请求的cURL命令后,接着利用以下网站将相应的cURL命令转换为Go语言代码。

Convert curl commands to code

对于请求结果的处理需要在Go中创建相应的结构体,手动编写非常麻烦,这里利用以下在线工具中的json2go工具直接生成对应结构体。

在线工具 - OKTools

这个项目整体难度也不大,但是其利用公共API的方式以及对于代码生成工具的利用非常值得学习。

项目三:SOCKS5代理

第三个小项目SOCKS5代理,这个项目代码量比前两个都要大也更为复杂,总计约200行。

首先简单介绍SOCK5代理的原理。

Untitled.png

代码总共四个函数,main(),process(),auth(),connect(),主要部分是认证函数auth()和连接函数connect()。代码内容大多都是些读取数据,在各种数据格式之间进行转换。这里不进行赘述,建议自行查看源代码理解。

课后习题

题目一

修改第一个例子猜谜游戏里的最终代码,使用fmt.Scanf()来简化代码实现。

首先到官网查询fmt.Scanf()的文档

Untitled 1.png

其实也就跟C语言里的scanf用法差不多,只不过额外返回了一个error用于错误处理。

直接列出修改前后的代码

Untitled 2.png 确实整个读取流程简化了不少,值得注意的是我这里使用的是Windows平台,所以换行符为\r\n,Linux和MacOS平台换行符应为\n

题目二

修改第二个例子命令行词典里的最终代码,增加另一种翻译引擎的支持。

我选择使用有道智云AI翻译,其API较为清晰简洁,容易按照课堂教学方法操作。

首先按照课程示例复制其cURL命令。

Untitled 3.png

然后使用curlconverter网站将请求转换为Go语言代码,对其代码略作修改,将解析结果存入相应的结构体,结构体转换使用json2go工具,最终有道词典查询代码如下

type DictResponseYoudao struct {
	ReturnPhrase []string `json:"returnPhrase"`
	Query        string   `json:"query"`
	ErrorCode    string   `json:"errorCode"`
	L            string   `json:"l"`
	TSpeakURL    string   `json:"tSpeakUrl"`
	Web          []struct {
		Value []string `json:"value"`
		Key   string   `json:"key"`
	} `json:"web"`
	RequestID   string   `json:"requestId"`
	Translation []string `json:"translation"`
	Dict        struct {
		URL string `json:"url"`
	} `json:"dict"`
	Webdict struct {
		URL string `json:"url"`
	} `json:"webdict"`
	Basic struct {
		ExamType   []string `json:"exam_type"`
		UsPhonetic string   `json:"us-phonetic"`
		Phonetic   string   `json:"phonetic"`
		UkPhonetic string   `json:"uk-phonetic"`
		Wfs        []struct {
			Wf struct {
				Name  string `json:"name"`
				Value string `json:"value"`
			} `json:"wf"`
		} `json:"wfs"`
		UkSpeech string   `json:"uk-speech"`
		Explains []string `json:"explains"`
		UsSpeech string   `json:"us-speech"`
	} `json:"basic"`
	IsWord   bool   `json:"isWord"`
	SpeakURL string `json:"speakUrl"`
}

func queryWithYoudao(word string) {
	client := &http.Client{}
	var data = strings.NewReader(fmt.Sprintf(`q=%s&from=en&to=zh-CHS`, word))
	req, err := http.NewRequest("POST", "https://aidemo.youdao.com/trans", data)
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("authority", "aidemo.youdao.com")
	req.Header.Set("accept", "application/json, text/javascript, */*; q=0.01")
	req.Header.Set("accept-language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
	req.Header.Set("content-type", "application/x-www-form-urlencoded; charset=UTF-8")
	req.Header.Set("dnt", "1")
	req.Header.Set("origin", "https://ai.youdao.com")
	req.Header.Set("referer", "https://ai.youdao.com/")
	req.Header.Set("sec-ch-ua", `" Not A;Brand";v="99", "Chromium";v="101", "Microsoft Edge";v="101"`)
	req.Header.Set("sec-ch-ua-mobile", "?0")
	req.Header.Set("sec-ch-ua-platform", `"Windows"`)
	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/101.0.4951.41 Safari/537.36 Edg/101.0.1210.32")
	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 DictResponseYoudao
	err = json.Unmarshal(bodyText, &dictResponse)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(word)
	for _, item := range dictResponse.Basic.Explains {
		fmt.Println(item)
	}
}

完整代码见Github链接

运行示意

Untitled 4.png

题目三

在上一步骤的基础上,修改代码实现并行请求两个翻译引擎来提高响应速度。

使用Go协程与WaitGroup来实现并发,只需对main()函数做一些修改如下

func main() {
	if len(os.Args) != 2 {
		fmt.Fprintf(os.Stderr, `usage: simpleDict WORD
Translation engine supports "youdao" and "caiyun"
example: simpleDict hello`)
		os.Exit(1)
	}
	word := os.Args[1]

	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		defer wg.Done()
		queryWithCaiyun(word)
	}()
	go func() {
		defer wg.Done()
		queryWithYoudao(word)
	}()
	wg.Wait()
}

运行结果

Untitled 5.png

这里的结果碰巧是将两个词典查询的结果轮流输出,不过其实这样的写法是不能保证输出结果按两个词典的顺序输出(可能先输出两行beautiful开头的行,然后再输出相应解释,即结果有可能交替)

正确的做法应该是利用管道进行通信,参考Go并发编程实战。这里还没学到并发编程,暂时不进行实现。