go语言的实战案例
1.猜字游戏
遇到bug1:
在建立包的时候没有用package main而用了别的名称,然后发现无法运行main函数了 解决方案:建立包的时候就用package main就可以了
在Go语言中,package main表示一个可以独立执行的程序,每个Go程序都包含一个名为main的包。这个包为特殊的包名,表示这个包为一个可执行程序的程序入口包。
遇到bug2:
在用输入流处理数据的时候报错: Unable to process evaluate: debuggee is running
自己打开终端使用命令
go run guess.go
时是可以正常的对输入流进行处理的。而vscode的一键调试却会报错。
问题原因:使用默认的配置文件它在调试时从console启动而不是terminal启动。通过修改配置文件的方法即可解决。
解决方案如下,需要在lunch.json中添加一行配置:"console": "integratedTerminal" ,设置调试时通过terminal启动,不通过console启动
将默认配置文件修改为:
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}",
"console": "integratedTerminal"
}
]
}
可以通过。
遇到bug3:
//获取输入流的代码:
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n') //找一下有没有\n换行符
if err != nil {
fmt.Println("ERROR WHEN FIND THE ", err)
return
}
input = strings.TrimSuffix(input, "\n")
//------------------------------------报错
guess, err := strconv.Atoi(input)
if err != nil {
fmt.Println("ERROR CONVERSE THE STRING TO INT ", input)
return
}
在对字符串流input进行Atoi时报错.一直输出报错栏里的提示信息:ERROR CONVERSE THE STRING TO INT
通过调试发现: 我们使用回车进行换行时代表回车符号'\r'也在其中,故删除后缀的时候得将回车符与换行符一块删去。
input = strings.TrimSuffix(input, "\r\n")
问题解决!
2.命令行字典
1.抓包
1.找到网页请求的dict
2.复制cURL
3.cURL转化为高级语言
Convert curl commands to code (curlconverter.com)
遇到bug1:
从网页请求复制的curl无法通过curlconverter转换为go语言。
//报错的源码:
curl "https://api.interpreter.caiyunai.com/v1/dict" ^
-H "authority: api.interpreter.caiyunai.com" ^
-H "accept: application/json, text/plain, */*" ^
-H "accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6" ^
-H "app-name: xy" ^
-H "content-type: application/json;charset=UTF-8" ^
-H "device-id: 9543d70d0bd80034298f8f8abe1aeafc" ^
-H "origin: https://fanyi.caiyunapp.com" ^
-H "os-type: web" ^
-H "os-version;" ^
-H "referer: https://fanyi.caiyunapp.com/" ^
-H "sec-ch-ua: ^^"Microsoft Edge^^";v=^^"113^^", ^^"Chromium^^";v=^^"113^^", ^^"Not-A.Brand^^";v=^^"24^^"" ^
-H "sec-ch-ua-mobile: ?0" ^
-H "sec-ch-ua-platform: ^^"Windows^^"" ^
-H "sec-fetch-dest: empty" ^
-H "sec-fetch-mode: cors" ^
-H "sec-fetch-site: cross-site" ^
-H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.50" ^
-H "x-authorization: token:qgemv4jr1y38jyq6vhvi" ^
--data-raw "^{^^"trans_type^^":^^"en2zh^^",^^"source^^":^^"good^^"^}" ^
--compressed
原因:在网上浏览最后在发现有人遇到了和我一样的问题:并且在GitHub的该工具的开发页面下提问: The curl command is correct but can't convert #failure to parse command · Issue #331 · curlconverter/curlconverter (github.com)
开发者下场解答:大致意思就是网页复制出来的空格是不间断空格(none-breaking-space)而不是正常的ASCII space。所以解决方案就是:`If you replace them with regular spaces
nnd暴力把空格删除光以后居然就可以生成代码了,ctmd。
curl "https://api.interpreter.caiyunai.com/v1/dict" ^-H "authority: api.interpreter.caiyunai.com" ^-H "accept: application/json, text/plain, */*" ^-H "accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6" ^-H "app-name: xy" ^-H "content-type: application/json;charset=UTF-8" ^-H "device-id: 9543d70d0bd80034298f8f8abe1aeafc" ^-H "origin: https://fanyi.caiyunapp.com" ^-H "os-type: web" ^-H "os-version;" ^-H "referer: https://fanyi.caiyunapp.com/" ^-H "sec-ch-ua: ^^"Microsoft Edge^^";v=^^"113^^", ^^"Chromium^^";v=^^"113^^", ^^"Not-A.Brand^^";v=^^"24^^"" ^-H "sec-ch-ua-mobile: ?0" ^-H "sec-ch-ua-platform: ^^"Windows^^"" ^-H "sec-fetch-dest: empty" ^-H "sec-fetch-mode: cors" ^-H "sec-fetch-site: cross-site" ^-H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.50" ^-H "x-authorization: token:qgemv4jr1y38jyq6vhvi" ^--data-raw "^{^^"trans_type^^":^^"en2zh^^",^^"source^^":^^"good^^"^}" ^--compressed
bug2来了:
在暴力删除完空格之后是可以生成代码了,但是仔细检查发现生成的代码并不符合要求,好多东西没生成出来,并且生成器还告诉你别的东西都没生成好。
怎么办?考虑是代码格式的问题。
最终解决了,无论如何都没有办法之后我到了谷歌上面重复了刚才的操作,您猜怎么着?
它很贴心的告诉我是不是用的Copy as cURL(cmd)啊?这是不行的,只有cURL(bash)才可以哦。
艹艹艹艹艹艹艹艹艹艹艹。
2.生成request body
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
)
type DictRequest struct {
TransType string `json:"trans_type"`
Source string `json:"source"`
UserID string `json:"user_id"`
}
func main() {
client := &http.Client{}
/*
var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)
//我们作为一个电子字典输入肯定不能是固定的字符串,所以我们用一个json流来充当输入
*/
//将字符串转化为输入流,为了节省内存
request := DictRequest{Source: "good", TransType: "en2zh"}
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-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
req.Header.Set("app-name", "xy")
req.Header.Set("content-type", "application/json;charset=UTF-8")
req.Header.Set("device-id", "9543d70d0bd80034298f8f8abe1aeafc")
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", `"Microsoft Edge";v="113", "Chromium";v="113", "Not-A.Brand";v="24"`)
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", "cross-site")
req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.50")
req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
//发起请求
resp, err := client.Do(req)
//如果因为断网,网络延迟等原因无法连接到服务器
if err != nil {
//杀死进程
log.Fatal(err)
}
//调用defer中的close函数来关闭流
defer resp.Body.Close()
//将流读入到内存中
bodyText, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", bodyText)
}
//输出一大段JSON
3.解析request body
将我们需要的信息从一大堆request body中解析出来。
思路:既然我们序列话的时候是创建一个结构体的实例对象并对它进行序列化,那么在反序列化的时候我们同样可以用一个结构体来接收json反序列化以后的各种变量并以一个结构体实例的方式去实现。
但是问题来了,一个字典的json所对应的结构体是相当复杂的,写起来很头疼。但是有软件可以贴心的根据输入json转化为它所需的结构体。JSON转Golang Struct - 在线工具 - OKTools
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
)
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 {
} `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 main() {
client := &http.Client{}
/*
var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)
//我们作为一个电子字典输入肯定不能是固定的字符串,所以我们用一个json流来充当输入
*/
//将字符串转化为输入流,为了节省内存
request := DictRequest{Source: "good", TransType: "en2zh"}
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-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
req.Header.Set("app-name", "xy")
req.Header.Set("content-type", "application/json;charset=UTF-8")
req.Header.Set("device-id", "9543d70d0bd80034298f8f8abe1aeafc")
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", `"Microsoft Edge";v="113", "Chromium";v="113", "Not-A.Brand";v="24"`)
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", "cross-site")
req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.50")
req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
//发起请求
resp, err := client.Do(req)
//如果因为断网,网络延迟等原因无法连接到服务器
if err != nil {
//杀死进程
log.Fatal(err)
}
//调用defer中的close函数来关闭流
defer resp.Body.Close()
//将流读入到内存中
bodyText, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
//防止没有产生err但是还是有错误存在的情况
if resp.StatusCode != 200 {
log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
}
/*
为了看是否转化json成功的输出测试
输出一大段json
fmt.Printf("%s\n", bodyText)
*/
//接收反序列化的结构体对象
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
log.Fatal(err)
}
/*
为了测试是否反序列化成功而写的输出,应该详细输出结构体的对象的每一个字段
fmt.Printf("%#v\n", dictResponse)
*/
//按需求格式化打印:
fmt.Print("UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
//利用range遍历打印Explanations数组:
for _, item := range dictResponse.Dictionary.Explanations {
fmt.Println(item)
}
}
4.完善代码:
直到目前为止我们已经基本上完成了样例的需求,但是我们还是通过传入固定的字符串充当参数,这肯定是不行的。
在原有代码的基础上做出了如下的改动:
1.将原来的main函数封装成一个函数query(查询),给其中传参string类型的参数word来代替上例中的"good"。
2.再新建一个main函数来调用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"`
UserID string `json:"user_id"`
}
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 []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 main() {
//如果在命令行输入时后面不是跟一个单词的话就错了
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, `usage:simpleDict WORD
elample: simpleDict hello
`)
os.Exit(1)
}
word := os.Args[1]
query(word)
}
func query(word string) {
client := &http.Client{}
/*
var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)
//我们作为一个电子字典输入肯定不能是固定的字符串,所以我们用一个json流来充当输入
*/
//将字符串转化为输入流,为了节省内存
request := DictRequest{Source: word, TransType: "en2zh"}
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-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
req.Header.Set("app-name", "xy")
req.Header.Set("content-type", "application/json;charset=UTF-8")
req.Header.Set("device-id", "9543d70d0bd80034298f8f8abe1aeafc")
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", `"Microsoft Edge";v="113", "Chromium";v="113", "Not-A.Brand";v="24"`)
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", "cross-site")
req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.50")
req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
//发起请求
resp, err := client.Do(req)
//如果因为断网,网络延迟等原因无法连接到服务器
if err != nil {
//杀死进程
log.Fatal(err)
}
//调用defer中的close函数来关闭流
defer resp.Body.Close()
//将流读入到内存中
bodyText, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
//防止没有产生err但是还是有错误存在的情况
if resp.StatusCode != 200 {
log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
}
/*
为了看是否转化json成功的输出测试
输出一大段json
fmt.Printf("%s\n", bodyText)
*/
//接收反序列化的结构体对象
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
log.Fatal(err)
}
/*
为了测试是否反序列化成功而写的输出,应该详细输出结构体的对象的每一个字段
fmt.Printf("%#v\n", dictResponse)
*/
//按需求格式化打印:
fmt.Print(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
//利用range遍历打印Explanations数组:
for _, item := range dictResponse.Dictionary.Explanations {
fmt.Println(item)
}
}