GO语言工程实践课后作业 | 青训营

105 阅读3分钟

作业---猜字谜

使用fmt.Scanf来简化猜字谜的输入操作

课程中的输入操作

reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')
input = strings.TrimSuffix(input, "\r\n")
guess, err := strconv.Atoi(input)

按照课程中的方法是用bufio的方式,从系统的stdin中读取内容到reader这个buf里

然后再从reader中读取字符串以\n结尾。接着用TrimSuffix函数镜像分割

由于Windows上的结尾符合为\r\n,而类unix系统的结尾符号都是\n,所以这里和视频里有所出入

获取到字符串之后,又使用strcov.Atoi函数,将字符串转化为数字

对比C语言中的scanf,可以看得出这一通操作很繁琐。

fmt.Scanf改进

var guess int
_, err := fmt.Scanf("%d\n", &guess)

可以看到相比bufio TrimSuffix Atoi的方式 改进后直接只用了一个Scanf便完成了相应的操作

作业---字典

增加一个Api调用

课程视频中使用的是彩云翻译的Api接口

而作业是用另外的接口实现,这里我选的是百度翻译api

type DictRequest struct {
	query string
	from  string
	to    string
	appid string
	salt  string
	sign  string
}

type DictResponse struct {
	From            string `json:"from"`
	To              string `json:"to"`
	TranslateResult []struct {
		Src string `json:"src"`
		Dst string `json:"dst"`
	} `json:"trans_result"`
}

func query(query string) (DictResponse, error) {
	request := DictRequest{
		query: query,
		from:  "auto",
		to:    "zh",
		appid: appid,
		salt:  salt,
		sign:  getSign(query),
	}
	data := url.Values{}
	data.Set("q", request.query)
	data.Set("from", request.from)
	data.Set("to", request.to)
	data.Set("appid", request.appid)
	data.Set("salt", request.salt)
	data.Set("sign", request.sign)
	u, err := url.ParseRequestURI(host)
	if err != nil {
		log.Fatal(err)
		return DictResponse{}, errors.New("bad response")
	}
	u.RawQuery = data.Encode()
	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		log.Fatal(err)
		return DictResponse{}, errors.New("bad response")
	}
	client := &http.Client{}
	req.Header.Add("Accept", "*/*")
	req.Header.Add("Host", "fanyi-api.baidu.com")
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	resp, err := client.Do(req)
	if resp.StatusCode != 200 {
		log.Fatal("bad StatusCode", resp.StatusCode)
		return DictResponse{}, errors.New("bad response")
	}
	if err != nil {
		log.Fatal(err)
		return DictResponse{}, errors.New("bad response")
	}
	defer resp.Body.Close()
	bodytext, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
		return DictResponse{}, errors.New("bad response")
	}
	var response DictResponse
	err = json.Unmarshal(bodytext, &response)
	if err != nil {
		log.Fatal(err)
		return DictResponse{}, errors.New("bad response")
	}
	return response, nil
}

这里只放了部分的代码,个人信息都没展示出来。

根据百度的Api手册,百度的翻译Api用Get方法将参数以param的形式传入,然后返回一个json作为结果

传入的参数有一个值得提一嘴的是sign,这个参数需要用百度提供的appid secret再配合需要查询的字符串和查询随机生成的一段字符串,通过字符串拼接的方式计算出MD5码作为sign传入,以达到服务校验的目的

两个api同时请求加快查询

通过同时请求两个api,哪个先得到结果,就显示哪个的结果,这样可以保证api请求的基本速度。

通过对api请求的封装我们可以将主逻辑简化为如下流程

func main() {
	word := getWord()
	results := make(chan common.Result, 1)
	go func() {
		var result common.Result
		var err error
		for {
			result, err = baiduapi.Query(word)
			if err == nil {
				break
			}
		}
		results <- result
	}()
	go func() {
		var result common.Result
		var err error
		for {
			result, err = caiyunapi.Query(word)
			if err == nil {
				break
			}
		}
		results <- result
	}()
	select {
	case result := <-results:
		fmt.Println(result.Src)
		fmt.Println(result.Dst)
		break
	}
}

我们定义了一个channel用作协程返回结果。开两个协程,分别对应两个api的请求操作

在主协程中,我们使用select监听result channel,一旦有协程得到结果向channel中添加了数据,直接将该数据打印