这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记。
课前导学链接
【Go 语言原理与实践学习资料】第三届字节跳动青训营-后端专场 - 掘金
本节课内容为Go语言基础和三个小项目实战,课程代码在以下网址中
课程PPT链接
Go语言基础
第一部分的内容基本可以在以下链接中找到,建议基础薄弱的同学阅读以下链接。
此外贴出一个我非常推荐的一个Go语言教程,Go语言之旅
小项目实战
项目一:猜谜游戏
第一个小项目猜数字游戏,这个项目比较简单,主要是联系一些基础语法使用,使用相应的包建议浏览官方文档,此项目用到了math/rand包。
项目二:简单词典
第二个小项目简单词典,利用彩云小译的查词API来查询英语单词。
首先展示了如何利用浏览器开发人员工具获取HTTP请求的详细信息(网络部分找到dict请求),复制dict请求的cURL命令后,接着利用以下网站将相应的cURL命令转换为Go语言代码。
对于请求结果的处理需要在Go中创建相应的结构体,手动编写非常麻烦,这里利用以下在线工具中的json2go工具直接生成对应结构体。
这个项目整体难度也不大,但是其利用公共API的方式以及对于代码生成工具的利用非常值得学习。
项目三:SOCKS5代理
第三个小项目SOCKS5代理,这个项目代码量比前两个都要大也更为复杂,总计约200行。
首先简单介绍SOCK5代理的原理。
代码总共四个函数,main(),process(),auth(),connect(),主要部分是认证函数auth()和连接函数connect()。代码内容大多都是些读取数据,在各种数据格式之间进行转换。这里不进行赘述,建议自行查看源代码理解。
课后习题
题目一
修改第一个例子猜谜游戏里的最终代码,使用fmt.Scanf()来简化代码实现。
首先到官网查询fmt.Scanf()的文档,
其实也就跟C语言里的scanf用法差不多,只不过额外返回了一个error用于错误处理。
直接列出修改前后的代码
确实整个读取流程简化了不少,值得注意的是我这里使用的是Windows平台,所以换行符为
\r\n,Linux和MacOS平台换行符应为\n。
题目二
修改第二个例子命令行词典里的最终代码,增加另一种翻译引擎的支持。
我选择使用有道智云AI翻译,其API较为清晰简洁,容易按照课堂教学方法操作。
首先按照课程示例复制其cURL命令。
然后使用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链接。
运行示意
题目三
在上一步骤的基础上,修改代码实现并行请求两个翻译引擎来提高响应速度。
使用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()
}
运行结果
这里的结果碰巧是将两个词典查询的结果轮流输出,不过其实这样的写法是不能保证输出结果按两个词典的顺序输出(可能先输出两行beautiful开头的行,然后再输出相应解释,即结果有可能交替)
正确的做法应该是利用管道进行通信,参考Go并发编程实战。这里还没学到并发编程,暂时不进行实现。