GO语言实战案例 猜数字&在线词典
前置
笔者环境
- macos 10.15.7
- Golang 1.18
- GoLand 2022.01
读完本文可以获得
- 标准输入的读取与处理
- 随机生成指定区间的整数
- 发送Http请求
- 解析JSON
- 代码生成工具
一、 猜谜游戏
项目效果
随机产生一个数值(1-100以内),用户通过控制台输入数字,程序处理输入并提示用户相关信息(比随机大、小或是猜对了)。
学习内容
-
标准输入的读取与处理
-
随机生成指定区间的整数
1.1 通过bufio.Reader读取输入
bufio.Reader
bufio.Reader是Go语言bufio包下提供的带缓冲的Reader实现,可以有效提高文本IO的效率。
主要的功能和优点有:
-
缓冲读取 -
bufio.Reader使用内部缓冲区,减少读取次数,提高效率。 -
非阻塞读取 - 支持设置超时,可以实现非阻塞IO。
-
文本读取 - 支持按行读取字符串,方便处理文本输入。
-
灵活读取 - 提供Peek,ReadBytes等多种读取方法,可以灵活控制读取。
-
错误处理 - 所有操作返回错误对象,可以直接判断。
使用bufio.Reader主要分3步:
- 创建
Reader对象:
reader := bufio.NewReader(os.Stdin)
-
调用读取方法,如
ReadString()等。 -
使用错误检查。
例如:
reader := bufio.NewReader(os.Stdin)
line, err := reader.ReadString('\n')
if err != nil {
return err
}
这样就可以从标准输入缓冲读取一行文本了。
功能实现
通过读取标准输入并转换为int类型,并与随机数值进行比较,并提示用户相关信息。同时通过判断error异常信息,来确保用户输入的值是个整数。
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
"strconv"
"strings"
)
func main() {
maxNum := 101 // 随机数的最大值
// rand.Seed在1.20版本之前,默认的随机数生成器是rand.Seed(1);1.20版本后,默认的随机数生成器会在程序启动时随机地初始化种子值,保证保证每次启动的随机数序列都不同。
// rand.Seed(time.Now().UnixNano()) // 使用启动时间戳随机初始化种子,保证每次启动的随机数序列都不同。
secretNum := rand.Intn(maxNum) // 返回[0,101)直接的随机数
for {
fmt.Print("Please input your guess:")
reader := bufio.NewReader(os.Stdin) // 创建一个从标准输入读取数据的缓冲读取器
input, err := reader.ReadString('\n') // 读取用户输入的一行字符串,直到遇到换行符’\n’
if err != nil {
fmt.Println("An error occurred while reading input. Please try again.")
continue
}
input = strings.TrimSuffix(input, "\n") // 去掉input字符串末尾的换行符
guess, err := strconv.Atoi(input) // 将input字符串转换为int类型
if err != nil {
fmt.Println("Invalid input. Please enter an integer between 1 and 100.")
continue
}
if guess > secretNum {
fmt.Println("Your guess is greater than the secret number. Please try again.")
} else if guess < secretNum {
fmt.Println("Your guess is less than the secret number. Please try again.")
} else {
fmt.Println("Correct, you legend!")
break
}
}
}
1.2 通过fmt.Scanf读取输入
fmt.Scanf
fmt.Scanf()是Go语言fmt包下的一个输入函数,用于从标准输入中读取格式化的文本输入。类似与C语言的scanf()函数。
基本使用格式:
fmt.Scanf(format string, a ...interface{})
其中format参数指定了格式控制符,后面可以跟多个变量,用于接收输入的数据。
例如:
fmt.Scanf("%d %s", &num, &name)
这会从标准输入读取一个整数到num,以及一个字符串到name。
fmt.Scanf会阻塞等待用户输入,输入回车后才会返回结果。
常用的格式控制符有:
-
%d - 十进制整数
-
%x - 十六进制整数
-
%f - 浮点数
-
%s - 字符串
fmt.Scanf会跳过空白,遇到匹配的格式才会读取输入。
与之相似的函数还有fmt.Scan()和fmt.Scanln(),不同的是它们会以空格分割,而Scanf可以指定格式。
功能实现
package main
import (
"bufio"
"fmt"
"math/rand"
"os"
)
func main() {
maxNum := 101 // 随机数的最大值
secretNum := rand.Intn(maxNum) // 返回[0,101)直接的随机数
guess := 0 // 用户猜的数字
for {
fmt.Print("Please input your guess:")
_, err := fmt.Scanf("%d", &guess) // 读取用户输入的数字
if err != nil {
fmt.Println("Invalid input. Please enter an integer between 1 and 100.")
bufio.NewReader(os.Stdin).ReadBytes('\n') // 清空缓冲区,避免读取到多余的字符,如错误输入后的换行
continue
}
if guess > secretNum {
fmt.Println("Your guess is greater than the secret number. Please try again.")
} else if guess < secretNum {
fmt.Println("Your guess is less than the secret number. Please try again.")
} else {
fmt.Println("Correct, you legend!")
break
}
}
}
1.3 差异
| 差异点 | fmt.Scanf() | bufio.NewReader |
|---|---|---|
| 读取方式 | 通过格式字符串读取 | 通过ReadString等方法读取 |
| 处理空格 | 跳过空白字符 | 保留空格和换行符 |
| 错误处理 | 返回读取的数目 | 返回错误对象 |
| 阻塞方式 | 完全阻塞 | 支持超时非阻塞 |
| 缓存机制 | 无缓存 | 使用缓冲区缓存 |
总结一下:
- fmt.Scanf() 通过格式化读取输入,遇到匹配才读取
- bufio.NewReader 通过缓冲和非格式化方式读取
- 前者读取方式简单直接,后者更灵活可控
二、 在线词典
项目效果
通过读取命令行参数,调用第三方API查询到单词的翻译并打印出来
新增一个引擎
学习内容
- 发送Http请求
- 解析JSON
- 代码生成
2.1 抓包
-
-
点击检查或
F12进入控制台功能,并点击网络选项 -
在翻译框中输入“hello”,发现下方出现的若干个
dict名称的条目 -
点击
dict名称的条目,找到POST请求 -
点击预览,我们想的信息是dictionary里的explanations字段
-
我们要在Go里面发这个Http的请求,然后显示explanations的信息。因此右键点击名称下的
dict,复制为cURL复制完成后,cURL命令如下
curl 'https://api.interpreter.caiyunai.com/v1/dict' \ -H 'authority: api.interpreter.caiyunai.com' \ -H 'accept: application/json, text/plain, */*' \ -H 'accept-language: zh,en;q=0.9' \ -H 'app-name: xy' \ -H 'content-type: application/json;charset=UTF-8' \ -H 'device-id: 02374c968dc8ea87ec9f04d6890e8bd7' \ -H 'origin: https://fanyi.caiyunapp.com' \ -H 'os-type: web' \ -H 'os-version;' \ -H 'referer: https://fanyi.caiyunapp.com/' \ -H 'sec-ch-ua: "Not.A/Brand";v="8", "Chromium";v="114", "Microsoft Edge";v="114"' \ -H 'sec-ch-ua-mobile: ?0' \ -H 'sec-ch-ua-platform: "macOS"' \ -H 'sec-fetch-dest: empty' \ -H 'sec-fetch-mode: cors' \ -H 'sec-fetch-site: cross-site' \ -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.67' \ -H 'x-authorization: token:qgemv4jr1y38jyq6vhvi' \ --data-raw '{"trans_type":"en2zh","source":"hello"}' \ --compressed -
进入Convert curl commands to Go (curlconverter.com),该网页可以运行
curl命令,并生成Http请求的代码。我们选择生成Go语言的代码。 -
将代码copy到IDE中,然后进行编译运行
如果有报错,可能是由于有几个header比较复杂,生成的代码有转义导致的错误,将标红的代码删除再次编译运行即可。
2.2 发送HTTP请求
HTTP请求的基本过程
// 创建HTTP客户端
client := &http.Client{}
// 构造请求Body
var data = strings.NewReader(`{"trans_type":"en2zh","source":"hello"}`)
// 创建POST请求,指定翻译API地址
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
// 设置请求头信息
req.Header.Set("Content-Type", "application/json")
// 发送请求
resp, err := client.Do(req)
// 关闭响应体
// Close() 是在逻辑上关闭,允许资源重用。但物理上数据仍在,可以读取。只有等 Go 垃圾回收时,内存才会真正释放。
defer resp.Body.Close()
//读取响应体
body,err := io.ReadAll(resp.Body)
// 打印翻译结果
fmt.Println(string(body))
HTTP请求基本流程是:
-
创建client对象,它负责处理请求细节
-
构造请求数据,如JSON Body
-
用http.NewRequest创建请求对象,指定方法、URL等
-
设置请求头部信息,如Content-Type等
-
使用client.Do发送请求
-
处理响应,一般需要读取resp.Body
-
关闭响应体
-
对响应数据进行处理
通过这些步骤,我们就可以通过HTTP请求获取API的数据了。
更改请求体
-
我们运行项目后,控制台打印了响应体的内容,是一段JSON,表示请求发送成功
{"rc":0,"wiki":{},"dictionary":{"prons":{"en-us":"[h\u0259\u02c8lo]","en":"[\u02c8he\u02c8l\u0259u]"},"explanations":["int.\u5582;\u54c8\u7f57","n.\u5f15\u4eba\u6ce8\u610f\u7684\u547c\u58f0","v.\u5411\u4eba\u547c(\u5582)"],"synonym":["greetings","salutations"],"antonym":[],"wqx_example":[["say hello to","\u5411\u67d0\u4eba\u95ee\u5019,\u548c\u67d0\u4eba\u6253\u62db\u547c"],["Say hello to him for me . ","\u4ee3\u6211\u95ee\u5019\u4ed6\u3002"]],"entry":"hello","type":"word","related":[],"source":"wenquxing"}} -
我们的请求体目前是固定的,意味着我们的单词只能是
hello,请求体是一个JSON,我们需要用变量来代替单词,然后构建一个JSON,并设置请求体var data = strings.NewReader(`{"trans_type":"en2zh","source":"hello"}`) -
该请求体有三个字段,我们可以构建一个结构体(需要设置结构体标签,如
json:"trans_type"),使用变量来代替单词然后组装结构体,然后通过json.Marshal将结构体序列化成JSON,因为json.Marshal返回的是一个字节数组,因此我们需要使用bytes.NewReader来读取字节数组并返回Reader流// 结构体 type DictRequest struct { TransType string `json:"trans_type"` Source string `json:"source"` } // 请求体 client := &http.Client{} word := "snorkeling" request := DictRequest{TransType: "en2zh", Source: word} buf, err := json.Marshal(request) if err != nil { log.Fatal(err) } var data = bytes.NewReader(buf) -
再次运行项目,我们发现已经可以根据调整word的值来返回不同的结果
2.3 解析JSON
下面我们就要将返回的响应体进行解析,常用的解析做法是写一个结构体,然后一一对应响应体里面的字段,然后使用json.Unmarshal将响应体反序列化到结构体。
但由于我们的响应体非常有很多字段,手动写效率低且容易出错,我们因此常使用代码生成工具进行JSON数据的解析。
-
进入JSON转Golang Struct - 在线工具 - OKTools,并粘贴我们的JSON,如果不需要对转换的结果进行操作,则可以选择
转换嵌套,让代码更加的紧凑{"rc":0,"wiki":{},"dictionary":{"prons":{"en-us":"[\u02c8sn\u0254rkl]","en":"[\u02c8sn\u0254\u02d0kl]"},"explanations":["n.(\u6f5c\u6c34\u8247\u7684)\u6c34\u4e0b\u901a\u6c14\u7ba1;(\u6f5c\u6e38\u8005\u4f7f\u7528\u7684)\u6c34\u4e0b\u547c\u5438\u7ba1"],"synonym":[],"antonym":[],"wqx_example":[],"entry":"snorkel","type":"word","related":[],"source":"wenquxing"}}
转换后的结构体
type AutoGenerated struct { Rc int `json:"rc"` Wiki struct { } `json:"wiki"` Dictionary struct { Prons struct { EnUs string `json:"en-us"` En string `json:"en"` } `json:"prons"` Explanations []string `json:"explanations"` Synonym []interface{} `json:"synonym"` Antonym []interface{} `json:"antonym"` WqxExample []interface{} `json:"wqx_example"` Entry string `json:"entry"` Type string `json:"type"` Related []interface{} `json:"related"` Source string `json:"source"` } `json:"dictionary"` } -
将转换后的结构体粘贴到IDE中,并修改名称为
DictResponsetype DictResponse struct { Rc int `json:"rc"` Wiki struct { } `json:"wiki"` Dictionary struct { Prons struct { EnUs string `json:"en-us"` En string `json:"en"` } `json:"prons"` Explanations []string `json:"explanations"` Synonym []interface{} `json:"synonym"` Antonym []interface{} `json:"antonym"` WqxExample []interface{} `json:"wqx_example"` Entry string `json:"entry"` Type string `json:"type"` Related []interface{} `json:"related"` Source string `json:"source"` } `json:"dictionary"` } -
使用
json.Unmarshal将响应体反序列化到结构体DictResponse上var dictResponse DictResponse err = json.Unmarshal(bodyText, &dictResponse) if err != nil { log.Fatal(err) } fmt.Printf("%#v\n", dictResponse) -
下面我们就要
DictResponse中需要的字段然后打印出来。我们需要的字段是Dictionary.Explanations,它是一个数组,里面包含单词的中文解释,使用for range取出并输出。还需要Dictionary.prons,他是一个结构体,里面包含英式和美式的音标,将它们取出后并进行输出。fmt.Println(word) fmt.Println("彩云小译", "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs) for _, item := range dictResponse.Dictionary.Explanations { fmt.Println(item) } -
我们再次运行项目,发现已经成功打印了我们想要的结果
snorkeling 彩云小译 UK: [ˈsnɔːkl] US: [ˈsnɔrkl] n.(潜水艇的)水下通气管;(潜游者使用的)水下呼吸管
2.4 获取命令行参数
项目要求我们通过命令行参数来指定单词,因此我们要获取命令行参数,并将结果赋值为变量word
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, `usage: simpleDict WORD\nexample: simpleDict hello`)
os.Exit(1)
}
word := os.Args[1]
client := &http.Client{}
...
}
在控制台输入go run xxx.go hello,xxx.go为你当前的go文件,即可看到结果
2.5 代码整合
我们发现main函数太臃肿了,它应该只承担数据的输入和输出,因此我们抽象出函数query(),将处理部分放入到query函数中
完整代码示例
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
)
type DictRequest struct {
TransType string `json:"trans_type"`
Source string `json:"source"`
}
type DictResponse struct {
Rc int `json:"rc"`
Wiki struct {
} `json:"wiki"`
Dictionary struct {
Prons struct {
EnUs string `json:"en-us"`
En string `json:"en"`
} `json:"prons"`
Explanations []string `json:"explanations"`
Synonym []interface{} `json:"synonym"`
Antonym []interface{} `json:"antonym"`
WqxExample []interface{} `json:"wqx_example"`
Entry string `json:"entry"`
Type string `json:"type"`
Related []interface{} `json:"related"`
Source string `json:"source"`
} `json:"dictionary"`
}
func query(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("authority", "api.interpreter.caiyunai.com")
req.Header.Set("accept", "application/json, text/plain, */*")
req.Header.Set("accept-language", "zh,en;q=0.9")
req.Header.Set("app-name", "xy")
req.Header.Set("content-type", "application/json;charset=UTF-8")
req.Header.Set("device-id", "02374c968dc8ea87ec9f04d6890e8bd7")
req.Header.Set("origin", "https://fanyi.caiyunapp.com")
req.Header.Set("os-type", "web")
req.Header.Set("os-version", "")
req.Header.Set("referer", "https://fanyi.caiyunapp.com/")
req.Header.Set("sec-ch-ua", `"Not.A/Brand";v="8", "Chromium";v="114", "Microsoft Edge";v="114"`)
req.Header.Set("sec-ch-ua-mobile", "?0")
req.Header.Set("sec-ch-ua-platform", `"macOS"`)
req.Header.Set("sec-fetch-dest", "empty")
req.Header.Set("sec-fetch-mode", "cors")
req.Header.Set("sec-fetch-site", "cross-site")
req.Header.Set("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.67")
req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
bodyText, err := io.ReadAll(resp.Body)
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
log.Fatal(err)
}
fmt.Println(word)
fmt.Println("彩云小译", "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
for _, item := range dictResponse.Dictionary.Explanations {
fmt.Println(item)
}
}
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, `usage: simpleDict WORD\nexample: simpleDict hello`)
os.Exit(1)
}
word := os.Args[1]
query(word)
}
在控制台输入go run xxx.go hello,即可看到结果
spaceqi@192 simpleDict % go run main.go hello
hello
彩云小译 UK: [ˈheˈləu] US: [həˈlo]
int.喂;哈罗
n.引人注意的呼声
v.向人呼(喂)
2.6 增加新的翻译引擎
-
使用有道翻译引擎产品文档-自然语言翻译服务 (youdao.com),用户需要先注册有道智云,然后开通文本翻译服务。
-
运行项目,将控制台输出的响应体复制出来,然后生成响应体结构体,然后将响应体使用反序列化到结构体上。
-
使用并行处理两个翻译引擎
-
完整代码
package main import ( "bytes" "crypto/rand" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "io" "log" "net/http" neturl "net/url" "os" "strconv" "strings" "time" ) type DictRequestCAIYUN struct { TransType string `json:"trans_type"` Source string `json:"source"` } type DictResponseCAIYUN struct { Rc int `json:"rc"` Wiki struct { } `json:"wiki"` Dictionary struct { Prons struct { EnUs string `json:"en-us"` En string `json:"en"` } `json:"prons"` Explanations []string `json:"explanations"` Synonym []interface{} `json:"synonym"` Antonym []interface{} `json:"antonym"` WqxExample []interface{} `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 := DictRequestCAIYUN{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("authority", "api.interpreter.caiyunai.com") req.Header.Set("accept", "application/json, text/plain, */*") req.Header.Set("accept-language", "zh,en;q=0.9") req.Header.Set("app-name", "xy") req.Header.Set("content-type", "application/json;charset=UTF-8") req.Header.Set("device-id", "02374c968dc8ea87ec9f04d6890e8bd7") req.Header.Set("origin", "https://fanyi.caiyunapp.com") req.Header.Set("os-type", "web") req.Header.Set("os-version", "") req.Header.Set("referer", "https://fanyi.caiyunapp.com/") req.Header.Set("sec-ch-ua", `"Not.A/Brand";v="8", "Chromium";v="114", "Microsoft Edge";v="114"`) req.Header.Set("sec-ch-ua-mobile", "?0") req.Header.Set("sec-ch-ua-platform", `"macOS"`) req.Header.Set("sec-fetch-dest", "empty") req.Header.Set("sec-fetch-mode", "cors") req.Header.Set("sec-fetch-site", "cross-site") req.Header.Set("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.67") req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi") resp, err := client.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() bodyText, err := io.ReadAll(resp.Body) var dictResponse DictResponseCAIYUN err = json.Unmarshal(bodyText, &dictResponse) if err != nil { log.Fatal(err) } fmt.Println(word) fmt.Println("彩云小译", "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs) for _, item := range dictResponse.Dictionary.Explanations { fmt.Println(item) } } type DictYOUDAO 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"` Translation []string `json:"translation"` MTerminalDict struct { URL string `json:"url"` } `json:"mTerminalDict"` Dict struct { URL string `json:"url"` } `json:"dict"` Weict struct { URL string `json:"url"` } `json:"weict"` Basic struct { ExamType []string `json:"exam_type"` UsPhonetic string `json:"us-phonetic"` Phonetic string `json:"phonetic"` UkSpeech string `json:"uk-speech"` Explains []string `json:"explains"` } `json:"basic"` IsWord bool `json:"isWord"` SpeakURL string `json:"speakUrl"` } func AddAuthParams(appKey string, appSecret string, params map[string][]string) { qs := params["q"] if qs == nil { qs = params["img"] } var q string for i := range qs { q += qs[i] } salt := getUuid() curtime := strconv.FormatInt(time.Now().Unix(), 10) sign := CalculateSign(appKey, appSecret, q, salt, curtime) params["appKey"] = []string{appKey} params["salt"] = []string{salt} params["curtime"] = []string{curtime} params["signType"] = []string{"v3"} params["sign"] = []string{sign} } func CalculateSign(appKey string, appSecret string, q string, salt string, curtime string) string { strSrc := appKey + getInput(q) + salt + curtime + appSecret return encrypt(strSrc) } func encrypt(strSrc string) string { bt := []byte(strSrc) bts := sha256.Sum256(bt) return hex.EncodeToString(bts[:]) } func getInput(q string) string { str := []rune(q) strLen := len(str) if strLen <= 20 { return q } else { return string(str[:10]) + strconv.Itoa(strLen) + string(str[strLen-10:]) } } func getUuid() string { b := make([]byte, 16) _, err := io.ReadFull(rand.Reader, b) if err != nil { return "" } b[6] = (b[6] & 0x0f) | 0x40 b[8] = (b[8] & 0x3f) | 0x80 return fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) } func createRequestParams(word string) map[string][]string { from := "en" to := "zh-CHS" return map[string][]string{ "q": {word}, "from": {from}, "to": {to}, } } func queryYOUDAO(word string) { // 您的应用ID appKey := "您的应用ID" // 您的应用密钥 appSecret := "您的应用密钥" // 添加请求参数 paramsMap := createRequestParams(word) header := map[string][]string{ "Content-Type": {"application/x-www-form-urlencoded"}, } // 添加鉴权相关参数 AddAuthParams(appKey, appSecret, paramsMap) // 请求api服务 result := DoPost("https://openapi.youdao.com/api", header, paramsMap, "application/json") // 将返回结果反序列化到结构体 var dictYOUDAO DictYOUDAO json.Unmarshal(result, &dictYOUDAO) fmt.Println(word) fmt.Println("有道翻译", "UK:", dictYOUDAO.Basic.Phonetic, "US:", dictYOUDAO.Basic.UsPhonetic) for _, item := range dictYOUDAO.Basic.Explains { fmt.Println(item) } } func DoPost(url string, header map[string][]string, bodyMap map[string][]string, expectContentType string) []byte { client := &http.Client{ Timeout: time.Second * 3, } params := neturl.Values{} for k, v := range bodyMap { for pv := range v { params.Add(k, v[pv]) } } req, _ := http.NewRequest("POST", url, strings.NewReader(params.Encode())) for k, v := range header { for hv := range v { req.Header.Add(k, v[hv]) } } res, err := client.Do(req) if err != nil { fmt.Print("request failed:", err) return nil } defer res.Body.Close() body, _ := io.ReadAll(res.Body) contentType := res.Header.Get("Content-Type") if !strings.Contains(contentType, expectContentType) { print(string(body)) return nil } return body } func main() { if len(os.Args) != 2 { fmt.Fprintf(os.Stderr, `usage: simpleDict WORD\nexample: simpleDict hello`) os.Exit(1) } word := os.Args[1] var wg sync.WaitGroup wg.Add(2) // 等待2个goroutine go func() { defer wg.Done() queryCAIYUN(word) }() go func() { defer wg.Done() queryYOUDAO(word) }() wg.Wait() // 等待goroutine结束 }运行结果
spaceqi@192 simpleDictHomeWork % go run main.go snorkeling snorkeling 彩云小译 UK: [ˈsnɔːkl] US: [ˈsnɔrkl] n.(潜水艇的)水下通气管;(潜游者使用的)水下呼吸管 snorkeling 有道翻译 UK: ˈsnɔ:klɪŋ US: ˈsnɔːrkəlɪŋ n. 浮潜;潜水;浅滩潜水 v. 用水下通气管潜航;用通气管潜泳(snorkel 的 ing 形式) adj. 潜浮的