GO 基础语法实战(作业篇)| 青训营笔记

151 阅读6分钟

GO 基础语法实战(作业篇)| 青训营笔记

系列介绍

哈哈哈,其实这个系列如题,就是黄同学也参加了这一届青训营😚。作为一名知识内容输出爱好者,我是非常喜欢这个活动的。在接下来的日子里我会持续更新这个系列,希望可以通过这个过程,将我在这次后端青训营的学习过程,尤其是针对课程中知识内容的课下实践,自己的心得体会,知识总结输出到掘金社区。💖

哈哈哈,其实也希望通过这个来对我这种懒狗的一种鞭策。🏅

目前该系列已经发布了:

  1. 👉Go快速上手之基础语法 | 青训营笔记 - 掘金 (juejin.cn) 👈
  2. 👉 Go语法实战个人拓展(词典) | 青训营笔记 - 掘金 (juejin.cn) 👈
  3. 👉 Go基础语法之实战 | 青训营笔记 - 掘金 (juejin.cn) 👈
  4. 👉 Go基础语法之实战(socks5代理)| 青训营笔记 - 掘金 (juejin.cn)👈

感兴趣的可以看看🌹

本文摘要

  1. 主要是针对官方的作业进行处理,分别是以下作业:

    1. 猜谜游戏fmt.Scanf 简化代码。
    2. 修改词典的代码,增加多一种翻译引擎支持。
    3. 在上面1.2 的代码基础上,使用并行请求翻译引擎。
  2. 针对这三个任务,给出黄同学的解决方式❇️😚

1. 猜谜游戏 代码简化

  1. 因为要使用 fmt.Scanf 简化代码,所以先看看它的原形:

    func Scanf(format string, a ...any) (n int, err error)
    

    以及文档中的描述,"Scanf scans text read from standard input, storing successive space-separated values into successive arguments as determined by the format. It returns the number of items successfully scanned. If that is less than the number of arguments, err will report why. Newlines in the input must match newlines in the format. The one exception: the verb %c always scans the next rune in the input, even if it is a space (or tab etc.) or newline."

  2. 其实如果你用过 CC++ 中的 scanf 函数,其实上手这个就很快,几乎是一样的。

  3. 原本的代码

        reader := bufio.NewReader(os.Stdin) /*创建从标准输入读取的缓冲读取器*/
        for true {
            input, err := reader.ReadString('\n') /*从读取器中读取并解析格式化的文本,这里读到换行符*/
            if err != nil {
                fmt.Println("An error occured while reading input. Please try again", err)
                continue
            }
            input = strings.Trim(input, "\r\n") /*去除末尾的换行符*/
            guess, err := strconv.Atoi(input)   /*转换为数字*/        
    

    这里是用了 bufio.NewReader(os.Stdin),从输入流中读取,但是如果我们用 fmt.Scanf 来实现就会很简单。

1.1 Go-Code

package main
​
import (
    "fmt"
    "math/rand"
    "time"
)
​
func main() {
    maxNum := 100
    rand.Seed(time.Now().UnixNano())
    secretNumber := rand.Intn(maxNum)
​
    fmt.Println("Please input your guess")
    for true {
        var guess int
        //var s string
        _, err := fmt.Scanf("%d\n", &guess)
        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 secret number. Please try again")
        } else if guess < secretNumber {
            fmt.Println("Your guess is smaller than secret number. Please try again")
        } else {
            fmt.Println("Correct, you Legend")
            break
        }
    }
}

2. 命令行翻译

黄同学此前已经实现了多次翻译了,在官方代码的基础上读取,感兴趣的可以👉Go快速上手之基础语法 | 青训营笔记 - 掘金 (juejin.cn) ​。​

2.1 加持新的翻译引擎——百度翻译

  1. 黄同学使用的是 百度翻译,但是使用百度翻译的话,不能向课程中直接用请求来获取,而是要去百度翻译官方获取appidkey,才能够使用,因为百度翻译的请求中包括了 token秘钥,且是url编码,有道等其他翻译也是同理。

  2. 百度翻译 API 申请:

    1. 进入百度翻译API网址在这里插入图片描述
    2. 登录后,如果是首次则需要申请认证,认证后就可以使用api了,在顶部栏中选择 产品服务通用文本翻译在这里插入图片描述
    3. 在接入文档中,就可以看到一些使用案例和api的使用方式。在这里插入图片描述
    4. api的输入参数在这里插入图片描述
    5. 在控制台,可以看到你的appid秘钥在这里插入图片描述

Go-Code

package main
​
import (
    "bytes"
    "crypto/md5"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "math/rand"
    "net/http"
    "strings"
)
​
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"`
}
​
func queryCaiyun(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 func(Body io.ReadCloser) {
        err := Body.Close()
        if err != nil {
            log.Fatal(err)
        }
    }(resp.Body)
    bodyText, err := io.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)
    }
}
​
const randMax = 1000000type BaiduTranslateResponse struct {
    From        string `json:"from"` /*源语言*/
    To          string `json:"to"`   /*目标语言*/
    TransResult []struct {
        Src string `json:"src"` /*原文*/
        Dst string `json:"dst"` /*译文*/
    } `json:"trans_result"` /*表示翻译结果*/
}
​
// 百度翻译
func queryBaidu(word string) {
    appid := ""                                         /*appid:向百度翻译官方可以申请获得*/
    key := ""                                          /*密钥同appid*/
    from := "en"                                       /*输入字符word的所属,这里表示为英文,可以设置auto*/
    to := "zh"                                         /*目标语言,必须设置,这里表示中文*/
    salt := string(rand.Intn(randMax))                 /*生成随机数*/
    sign := md5.Sum([]byte(appid + word + salt + key)) /*md5加密生成签名*/
    // 生成url
    url := fmt.Sprintf("https://fanyi-api.baidu.com/api/trans/vip/translate?q=%s&from=%s&to=%s&appid=%s&salt=%s&sign=%x", word, from, to, appid, salt, sign)
    // 发送请求,并获取响应
    resp, err := http.Get(url)
    if err != nil {
        log.Fatal(err)
    }
    defer func(Body io.ReadCloser) {
        err := Body.Close()
        if err != nil {
            log.Fatal(err)
        }
    }(resp.Body)
    bodyText, err := io.ReadAll(resp.Body) /*读取响应报文的数据*/
    if err != nil {
        log.Fatal(err)
    }
    // http状态码200 表示正确无问题
    if resp.StatusCode != 200 {
        log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
    }
    res, err := parseTranslateResponse(bodyText)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("百度翻译:")
    result := ""
    for _, item := range res.TransResult {
        result += item.Dst + "\n"
    }
    fmt.Println(result)
}
​
// 定义一个函数,用于将百度的bodyText 转换为结构体
func parseTranslateResponse(bodyText []byte) (*BaiduTranslateResponse, error) {
    res := &BaiduTranslateResponse{}     /*创建一个json结构体指针,存储转换结果*/
    err := json.Unmarshal(bodyText, res) /*调用反序列化函数,得到json数据*/
    if err != nil {
        return nil, err
    }
    return res, nil
}
​
const maxCount = 1000 /*最大循环次数*/func main() {
    fmt.Printf("You could translate no more than %d times\n", maxCount)
    var word string
    for i := 0; i < maxCount; i++ {
        fmt.Println("Please input a word you want translate. You could input -1 if you want break")
        _, err := fmt.Scanf("%s\n", &word)
        if err != nil {
            fmt.Println("An error occured while reading input. Please try again", err)
            continue
        }
        word = strings.Trim(word, "\r\n")
        if word == "-1" {
            break
        }
        fmt.Println("word:", word)
        queryCaiyun(word)
        queryBaidu(word)
    }
}
  1. 代码中的keyappid 需要朋友们自己去申请。
  2. 签名用md5算法生成,随机数可以直接指定,比如 “123456”等。
  3. 由于返回的是一个json数据,如果不做处理的话,你会得到一组数字,所以我在代码中写了 parseTranslateResponse 这个方法来解析从百度翻译返回json数据,当然也编写了对应的结构体

2.2 并行请求

  1. 就是希望实现多线程的请求,那我们就需要两个goroutine来实现调用即可。
  2. 此外我们可以 channel 来实现从 子线程到主线程通信。

Go-Code

package main
​
import (
    "bytes"
    "crypto/md5"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "math/rand"
    "net/http"
    "strconv"
    "strings"
)
​
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"`
}
​
// 修改原来的函数,向彩云翻译发送请求,并将结果发送到一个channel中
func queryCaiyun(word string, ch chan 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 func(Body io.ReadCloser) {
        err := Body.Close()
        if err != nil {
            log.Fatal(err)
        }
    }(resp.Body)
    bodyText, err := io.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)
    // 将响应结果转换为字符串,发送到channel中
    result := fmt.Sprintf("%s UK: %s US:%s\n", word, dictResponse.Dictionary.Prons.En, dictResponse.Dictionary.Prons.EnUs)
    for _, item := range dictResponse.Dictionary.Explanations {
        result += fmt.Sprintf("%s\n", item)
    }
    fmt.Println("彩云翻译:")
    ch <- result
}
​
const randMax = 1000000type BaiduTranslateResponse struct {
    From        string `json:"from"` /*源语言*/
    To          string `json:"to"`   /*目标语言*/
    TransResult []struct {
        Src string `json:"src"` /*原文*/
        Dst string `json:"dst"` /*译文*/
    } `json:"trans_result"` /*表示翻译结果*/
}
​
// 百度翻译
func queryBaidu(word string, ch chan string) {
    appid := ""                       /*appid:向百度翻译官方可以申请获得*/
    key := ""                      /*密钥同appid*/
    from := "en"                                       /*输入字符word的所属,这里表示为英文,可以设置auto*/
    to := "zh"                                         /*目标语言,必须设置,这里表示中文*/
    salt := strconv.Itoa(rand.Intn(randMax))           /*生成随机数*/
    sign := md5.Sum([]byte(appid + word + salt + key)) /*md5加密生成签名*/
    // 生成url
    url := fmt.Sprintf("https://fanyi-api.baidu.com/api/trans/vip/translate?q=%s&from=%s&to=%s&appid=%s&salt=%s&sign=%x", word, from, to, appid, salt, sign)
    // 发送请求,并获取响应
    resp, err := http.Get(url)
    if err != nil {
        log.Fatal(err)
    }
    defer func(Body io.ReadCloser) {
        err := Body.Close()
        if err != nil {
            log.Fatal(err)
        }
    }(resp.Body)
    bodyText, err := io.ReadAll(resp.Body) /*读取响应报文的数据*/
    if err != nil {
        log.Fatal(err)
    }
    // http状态码200 表示正确无问题
    if resp.StatusCode != 200 {
        log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
    }
    res, err := parseTranslateResponse(bodyText)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("百度翻译:")
    result := ""
    for _, item := range res.TransResult {
        result += item.Dst + "\n"
    }
    //fmt.Println(result)
    ch <- result
}
​
// 定义一个函数,用于将百度的bodyText 转换为结构体
func parseTranslateResponse(bodyText []byte) (*BaiduTranslateResponse, error) {
    res := &BaiduTranslateResponse{}     /*创建一个json结构体指针,存储转换结果*/
    err := json.Unmarshal(bodyText, res) /*调用反序列化函数,得到json数据*/
    if err != nil {
        return nil, err
    }
    return res, nil
}
​
const maxCount = 1000 /*最大循环次数*/func main() {
    fmt.Printf("You could translate no more than %d times\n", maxCount)
    var word string
    for i := 0; i < maxCount; i++ {
        fmt.Println("Please input a word you want translate. You could input -1 if you want break")
        _, err := fmt.Scanf("%s\n", &word)
        if err != nil {
            fmt.Println("An error occured while reading input. Please try again", err)
            continue
        }
        word = strings.Trim(word, "\r\n")
        if word == "-1" {
            break
        }
        fmt.Println("word:", word)
​
        ch := make(chan string) /*创建一个字符串类型的channel*/
        //两个goroutine,分别调用不同的函数,请求不同的翻译引擎
        go queryCaiyun(word, ch)
        go queryBaidu(word, ch)
        fmt.Println(<-ch)
        fmt.Println(<-ch)
    }
}

参考资料

黄同学在编写这篇文章,除了自己的实践外,还参考了不少资料。如果朋友想要通过我的这篇简陋笔记文章去探索那些可以称为宝玉或者 💎 般的知识,不妨通过下面的链接看看:

  1. pkg.go.dev/encoding/js…
  2. The Go Programming Language Specification - The Go Programming Language
  3. fmt package - fmt - Go Packages
  4. 百度翻译开放平台 (baidu.com)**